add websocket push
This commit is contained in:
parent
5eb73ca7a1
commit
b3a3cf5ec8
@ -29,6 +29,10 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.chyxion.tigon</groupId>
|
||||
<artifactId>tigon-web-controller</artifactId>
|
||||
</dependency>
|
||||
<!-- Log -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -2,7 +2,7 @@ package com.pudonghot.ambition.crm.auth;
|
||||
|
||||
import lombok.val;
|
||||
import java.util.*;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import me.chyxion.tigon.shiro.AuthRealm;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@ -62,13 +62,13 @@ public class AuthRealmSupport implements AuthRealm<Principal, String> {
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> roles(final Principal principal) {
|
||||
val user = userService.find(new Search(User.EMPLOYEE_ID, principal));
|
||||
val user = userService.find(principal.getUserId());
|
||||
val roles = new ArrayList<String>(4);
|
||||
if (user.isAdmin()) {
|
||||
roles.add(User.ROLE_ADMIN);
|
||||
}
|
||||
val account = user.getAccount();
|
||||
if (ArrayUtils.contains(new String[] {User.ROLE_LELI, User.ROLE_CHYXION}, account)) {
|
||||
if (ArrayUtils.contains(new String[] {User.ROLE_LELI, User.ROLE_DONGHUANG}, account)) {
|
||||
roles.add(account);
|
||||
}
|
||||
return roles;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
package com.pudonghot.ambition.crm.common.controller;
|
||||
|
||||
import com.pudonghot.ambition.crm.controller.AbstractBaseController;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import com.pudonghot.ambition.crm.model.User;
|
||||
import me.chyxion.tigon.shiro.service.AuthService;
|
@ -1,4 +1,4 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
package com.pudonghot.ambition.crm.common.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
@ -1,6 +1,7 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
package com.pudonghot.ambition.crm.common.controller;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import me.chyxion.tigon.form.FC2;
|
||||
import me.chyxion.tigon.model.M0;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
@ -1,4 +1,4 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
package com.pudonghot.ambition.crm.common.controller;
|
||||
|
||||
import me.chyxion.tigon.form.FC2;
|
||||
import me.chyxion.tigon.model.M0;
|
@ -1,7 +1,7 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
package com.pudonghot.ambition.crm.common.controller;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chyxion.tigon.model.M0;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import me.chyxion.tigon.model.ListResult;
|
||||
@ -19,8 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
* May 10, 2016 4:50:58 PM
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseQueryController<Model extends M0<String>>
|
||||
extends BaseController {
|
||||
public class BaseQueryController<Model> extends BaseController {
|
||||
|
||||
@Autowired
|
||||
protected BaseQueryService<String, Model> queryService;
|
@ -0,0 +1,21 @@
|
||||
package com.pudonghot.ambition.crm.common.request;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.chyxion.tigon.web.controller2.request.BaseListCtrlrReqApi;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jun 18, 2022 13:27:22
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class BaseListCtrlrReq implements BaseListCtrlrReqApi {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer start;
|
||||
private Integer limit;
|
||||
private String search;
|
||||
private String filters;
|
||||
private String orders;
|
||||
}
|
@ -177,7 +177,7 @@ public abstract class AbstractBaseController {
|
||||
}
|
||||
if (StringUtils.isNotBlank(strSearch)) {
|
||||
val orSearch = new Search();
|
||||
for (final String col : searchCols()) {
|
||||
for (val col : searchCols()) {
|
||||
orSearch.or(new Search().like(col, decodeLike(strSearch)));
|
||||
}
|
||||
search.and(orSearch);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import lombok.val;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
@ -1,20 +1,19 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import lombok.val;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import org.springframework.util.Assert;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import com.pudonghot.ambition.crm.model.User;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import me.chyxion.tigon.kit.bean.BeanService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import me.chyxion.tigon.shiro.service.AuthService;
|
||||
import com.pudonghot.ambition.crm.service.UserService;
|
||||
import com.pudonghot.ambition.crm.auth.SessionAbility;
|
||||
import com.pudonghot.ambition.crm.auth.model.AuthToken;
|
||||
import com.pudonghot.ambition.crm.auth.model.Principal;
|
||||
import lombok.val;
|
||||
import me.chyxion.tigon.kit.bean.BeanService;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.util.Assert;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import com.pudonghot.ambition.crm.model.User;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import me.chyxion.tigon.shiro.service.AuthService;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import com.pudonghot.ambition.crm.service.UserService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@ -45,8 +44,10 @@ public class AuthController implements SessionAbility {
|
||||
@RequestParam("password")
|
||||
String password) {
|
||||
|
||||
val user = userService.find(new Search(User.ACCOUNT, loginId));
|
||||
Assert.state(user != null, "Unknown account");
|
||||
val user = findUser(loginId);
|
||||
Assert.state(user != null, () -> "Incorrect account or password");
|
||||
Assert.state(user.getEnabled(), "Account is disabled, please contact admin");
|
||||
|
||||
val principal = new Principal();
|
||||
principal.setAccount(loginId);
|
||||
principal.setUserId(user.getId());
|
||||
@ -69,4 +70,12 @@ public class AuthController implements SessionAbility {
|
||||
public ViewModel<User> info() {
|
||||
return userService.findViewModel(getUserId());
|
||||
}
|
||||
|
||||
User findUser(final String loginId) {
|
||||
val user = userService.find(new Search(User.ACCOUNT, loginId));
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
return userService.find(new Search(User.EMPLOYEE_ID, loginId));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import com.pudonghot.ambition.crm.service.ExportTaskService;
|
||||
import lombok.val;
|
||||
import java.util.*;
|
||||
import javax.validation.Valid;
|
||||
@ -14,6 +16,7 @@ import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.pudonghot.ambition.crm.model.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import com.pudonghot.ambition.crm.model.Customer;
|
||||
@ -107,6 +110,9 @@ public class CustomerController
|
||||
Pair.of(CRITERION_COLS.get("issue"), Boolean.class));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private ExportTaskService exportTaskService;
|
||||
|
||||
@RequestMapping("/list")
|
||||
public ListResult<ViewModel<Customer>> list(
|
||||
@Min(0)
|
||||
@ -164,6 +170,12 @@ public class CustomerController
|
||||
return FileDownloadUtils.toResponseEntity(((CustomerService) queryService).exportCSV(getUserId()), null);
|
||||
}
|
||||
|
||||
@RequiresRoles(User.ROLE_ADMIN)
|
||||
@RequestMapping("/async-export")
|
||||
public void asyncExportCSV() {
|
||||
exportTaskService.doExport(getUserId());
|
||||
}
|
||||
|
||||
@RequiresRoles(User.ROLE_ADMIN)
|
||||
@PostMapping("/delete")
|
||||
public void delete(@NotBlank @RequestParam("id") String id) {
|
||||
|
@ -4,6 +4,8 @@ import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Max;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import javax.validation.constraints.Min;
|
||||
import me.chyxion.tigon.model.ListResult;
|
||||
|
@ -3,6 +3,8 @@ package com.pudonghot.ambition.crm.controller;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseController;
|
||||
import com.pudonghot.ambition.crm.model.User;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
@ -0,0 +1,69 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import lombok.val;
|
||||
import javax.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import com.pudonghot.ambition.crm.model.User;
|
||||
import me.chyxion.tigon.web.controller2.ArgQuery;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import com.pudonghot.ambition.crm.model.ExportTask;
|
||||
import com.pudonghot.ambition.crm.auth.SessionAbility;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import me.chyxion.tigon.web.controller2.BaseQueryController;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.pudonghot.ambition.crm.ws.service.WebSocketService;
|
||||
import me.chyxion.tigon.web.controller2.response.ListCtrlrResp;
|
||||
import com.pudonghot.ambition.crm.common.request.BaseListCtrlrReq;
|
||||
import com.pudonghot.ambition.crm.ws.model.ExportTaskUpdatedWsResp;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jun 18, 2022 09:47:30
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/export-task")
|
||||
public class ExportTaskController
|
||||
extends BaseQueryController<Long,
|
||||
BaseListCtrlrReq,
|
||||
ExportTask,
|
||||
ExportTask>
|
||||
implements SessionAbility {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@RequiresRoles(User.ROLE_ADMIN)
|
||||
public ListCtrlrResp<ExportTask> list(@Valid final BaseListCtrlrReq req) {
|
||||
return list(req, new Search());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void before(final ArgQuery<?> arg) {
|
||||
super.before(arg);
|
||||
val search = arg.getSearch();
|
||||
search.eq(ExportTask.CREATED_BY, getUserId());
|
||||
if (arg.getType() == ArgQuery.Type.LIST) {
|
||||
search.desc(ExportTask.DATE_CREATED);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private WebSocketService webSocketService;
|
||||
|
||||
@PostMapping("/test-push")
|
||||
@RequiresRoles(User.ROLE_ADMIN)
|
||||
public void testPush(@RequestBody ExportTaskUpdatedWsResp resp) {
|
||||
resp.setEmployeeKey(getUserId());
|
||||
webSocketService.publish(resp);
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
|
@ -2,6 +2,8 @@ package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chyxion.tigon.model.ViewModel;
|
||||
import javax.validation.constraints.Max;
|
||||
@ -30,7 +32,7 @@ public class ImportRecordController
|
||||
"u.name",
|
||||
"u.en_name",
|
||||
"u.account",
|
||||
"u.employee_id" );
|
||||
"u.employee_id");
|
||||
|
||||
@RequestMapping("/list")
|
||||
@RequiresRoles(User.ROLE_ADMIN)
|
||||
|
@ -4,6 +4,7 @@ import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import lombok.val;
|
||||
import org.springframework.util.Assert;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
@ -71,14 +72,14 @@ public class LocalProductController
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/create", method = RequestMethod.POST)
|
||||
public void create(
|
||||
@Valid LocalProductFormForCreate form) {
|
||||
((LocalProductService) queryService).create(form);
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/update", method = RequestMethod.POST)
|
||||
public void update(
|
||||
@Valid LocalProductFormForUpdate form) {
|
||||
@ -98,7 +99,7 @@ public class LocalProductController
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/add-image", method = RequestMethod.POST)
|
||||
public ViewModel<AttachedImage> addImage(
|
||||
@Valid LocalProductImageFormForCreate form) {
|
||||
@ -106,19 +107,19 @@ public class LocalProductController
|
||||
return new ViewModel<>(((LocalProductService) queryService).addImage(form));
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/remove-image", method = RequestMethod.POST)
|
||||
public void removeImage(@NotBlank @RequestParam("id") String id) {
|
||||
((LocalProductService) queryService).removeImage(id);
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/update-image", method = RequestMethod.POST)
|
||||
public void updateImage(@Valid LocalProductImageFormForUpdate form) {
|
||||
((LocalProductService) queryService).updateImage(form);
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/add-attachment", method = RequestMethod.POST)
|
||||
public ViewModel<AttachedFile> addAttachment(
|
||||
@Valid LocalProductAttachedFileFormForCreate form) {
|
||||
@ -131,13 +132,13 @@ public class LocalProductController
|
||||
((LocalProductService) queryService).removeAttachment(id);
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/update-attachment", method = RequestMethod.POST)
|
||||
public void updateImage(@Valid LocalProductAttachedFileFormForUpdate form) {
|
||||
((LocalProductService) queryService).updateAttachment(form);
|
||||
}
|
||||
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_DONGHUANG, User.ROLE_ADMIN}, logical = Logical.OR)
|
||||
@RequestMapping(value = "/delete", method = RequestMethod.POST)
|
||||
public void delete(@NotBlank @RequestParam("id") String id) {
|
||||
((LocalProductService) queryService).delete(id);
|
||||
|
@ -5,6 +5,8 @@ import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.pudonghot.ambition.crm.controller;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.pudonghot.ambition.crm.common.controller.BaseQueryController;
|
||||
import lombok.val;
|
||||
import javax.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,9 @@
|
||||
package com.pudonghot.ambition.crm.service;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import com.pudonghot.ambition.crm.model.ExportTask;
|
||||
import me.chyxion.tigon.service2.BaseQueryService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
@ -8,13 +11,20 @@ import org.springframework.validation.annotation.Validated;
|
||||
* @date May 29, 2022 21:24:42
|
||||
*/
|
||||
@Validated
|
||||
public interface ExportTaskService {
|
||||
public interface ExportTaskService extends BaseQueryService<Long, ExportTask> {
|
||||
|
||||
/**
|
||||
* do export
|
||||
*
|
||||
* @param operator
|
||||
* @param employeeKey employee key
|
||||
*/
|
||||
void doExport(@NotBlank String operator);
|
||||
void doExport(@NotBlank String employeeKey);
|
||||
|
||||
/**
|
||||
* notify task updated
|
||||
*
|
||||
* @param employeeKey employee key
|
||||
*/
|
||||
void notifyTaskUpdated(@NotBlank String employeeKey);
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,102 @@
|
||||
package com.pudonghot.ambition.crm.service.support;
|
||||
|
||||
import lombok.val;
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chyxion.tigon.mybatis.Search;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import com.pudonghot.ambition.crm.model.ExportTask;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import com.pudonghot.ambition.crm.mapper.ExportTaskMapper;
|
||||
import com.pudonghot.ambition.crm.service.CustomerService;
|
||||
import com.pudonghot.ambition.crm.service.ExportTaskService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.pudonghot.ambition.crm.ws.service.WebSocketService;
|
||||
import me.chyxion.tigon.service.support2.BaseQueryServiceSupport;
|
||||
import com.pudonghot.ambition.crm.enumeration.EnumExportTaskStatus;
|
||||
import com.pudonghot.ambition.crm.ws.model.ExportTaskUpdatedWsResp;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date May 29, 2022 21:29:06
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ExportTaskServiceSupport implements ExportTaskService {
|
||||
public class ExportTaskServiceSupport
|
||||
extends BaseQueryServiceSupport<Long, ExportTask, ExportTaskMapper, ExportTask>
|
||||
implements ExportTaskService {
|
||||
|
||||
private final ConcurrentMap<String, Long> LOCK_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
@Qualifier("exportTaskExecutor")
|
||||
private TaskExecutor threadPoolTaskExecutor;
|
||||
@Autowired
|
||||
private CustomerService customerService;
|
||||
@Value("${export.location:/data/export}")
|
||||
private String exportLocation;
|
||||
@Autowired
|
||||
private WebSocketService webSocketService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void doExport(final String operator) {
|
||||
public void doExport(final String employeeKey) {
|
||||
val now = Long.valueOf(System.currentTimeMillis());
|
||||
Assert.state(now.equals(LOCK_MAP.putIfAbsent(employeeKey, now)),
|
||||
"There is a running export task, please try again later!");
|
||||
|
||||
threadPoolTaskExecutor.execute(() ->{
|
||||
val exportFile = customerService.exportCSV(operator);
|
||||
try {
|
||||
val exportTask = new ExportTask();
|
||||
exportTask.setCreatedBy(employeeKey);
|
||||
exportTask.setDateCreated(new Date());
|
||||
exportTask.setStatus(EnumExportTaskStatus.RUNNING);
|
||||
mapper.insert(exportTask);
|
||||
|
||||
try {
|
||||
val exportFile = customerService.exportCSV(employeeKey);
|
||||
val distDir = new File(new File(exportLocation),
|
||||
DateFormatUtils.format(now, "yyyyMMdd"));
|
||||
FileUtils.moveFileToDirectory(exportFile, distDir, true);
|
||||
exportTask.setStatus(EnumExportTaskStatus.DONE);
|
||||
exportTask.setLocation(new File(distDir, exportFile.getName()).getPath());
|
||||
exportTask.setDateUpdated(new Date());
|
||||
mapper.update(exportTask);
|
||||
} catch (final Throwable e) {
|
||||
log.error("Export file error caused.", e);
|
||||
exportTask.setStatus(EnumExportTaskStatus.FAILED);
|
||||
exportTask.setNote(ExceptionUtils.getStackTrace(e));
|
||||
exportTask.setDateUpdated(new Date());
|
||||
mapper.update(exportTask);
|
||||
}
|
||||
|
||||
notifyTaskUpdated(employeeKey);
|
||||
}
|
||||
finally {
|
||||
LOCK_MAP.remove(employeeKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void notifyTaskUpdated(final String employeeKey) {
|
||||
webSocketService.publish(
|
||||
new ExportTaskUpdatedWsResp(employeeKey,
|
||||
mapper.count(new Search(ExportTask.CREATED_BY, employeeKey)
|
||||
.isFalse(ExportTask.DOWNLOADED))));
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,16 @@
|
||||
package com.pudonghot.ambition.crm.util;
|
||||
|
||||
import com.pudonghot.ambition.crm.exception.CVSFileImportingException;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.io.*;
|
||||
import lombok.val;
|
||||
import lombok.SneakyThrows;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
@ -27,7 +18,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
*/
|
||||
@Slf4j
|
||||
public class FileDownloadUtils {
|
||||
private static final AtomicBoolean lock = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* file to response entity
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.pudonghot.ambition.crm.ws.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import java.io.Serializable;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jun 17, 2022 15:01:49
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class BaseWsResp implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank
|
||||
private String employeeKey;
|
||||
@NotBlank
|
||||
private String type;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.pudonghot.ambition.crm.ws.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jun 17, 2022 15:01:49
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class ExportTaskUpdatedWsResp extends BaseWsResp {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer countUndownloaded;
|
||||
private Integer countFailed;
|
||||
|
||||
public ExportTaskUpdatedWsResp() {
|
||||
setType("EXPORT_TASK_UPDATED");
|
||||
}
|
||||
|
||||
public ExportTaskUpdatedWsResp(final String employeeKey, final Integer countUndownloaded) {
|
||||
this();
|
||||
this.countUndownloaded = countUndownloaded;
|
||||
setEmployeeKey(employeeKey);
|
||||
}
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
package com.pudonghot.ambition.crm.ws.service;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import com.pudonghot.ambition.crm.ws.model.BaseWsResp;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
@ -14,8 +17,16 @@ public interface WebSocketService {
|
||||
/**
|
||||
* publish websocket message
|
||||
*
|
||||
* @param employeeKey
|
||||
* @param payload
|
||||
* @param resp resp
|
||||
*/
|
||||
void publish(@NotNull @Valid BaseWsResp resp);
|
||||
|
||||
/**
|
||||
* publish websocket message
|
||||
*
|
||||
* @param employeeKey employee key
|
||||
* @param payload payload
|
||||
*/
|
||||
void publish(@NotBlank String employeeKey, @NotNull Object payload);
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.pudonghot.ambition.crm.ws.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.pudonghot.ambition.crm.ws.model.BaseWsResp;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.pudonghot.ambition.crm.ws.service.WebSocketService;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
@ -17,6 +18,16 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
@Autowired
|
||||
private SimpMessagingTemplate message;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void publish(final BaseWsResp resp) {
|
||||
publish(resp.getEmployeeKey(), resp);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void publish(final String employeeKey,
|
||||
final Object payload) {
|
||||
log.debug("Publish websocket message [{}] -> [{}].", employeeKey, payload);
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit a69a6519311d6a5ec932f2f7e1e71bb381a04a20
|
||||
Subproject commit 56a42f79cac7f8369bacf51e323242c0ae202e55
|
@ -0,0 +1,4 @@
|
||||
package com.pudonghot.ambition.crm;
|
||||
|
||||
public class Test {
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jun 18, 2022 10:07:26
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE mapper PUBLIC
|
||||
"-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.pudonghot.ambition.crm.mapper.ExportTaskMapper">
|
||||
</mapper>
|
@ -4,18 +4,20 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.Date;
|
||||
import java.io.Serializable;
|
||||
import me.chyxion.tigon.mybatis.BasicEntity;
|
||||
import me.chyxion.tigon.mybatis.NotUpdate;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
/**
|
||||
* @author Shaun Chyxion <br>
|
||||
* chyxion@163.com <br>
|
||||
* Aug 09, 2017 22:24:40
|
||||
* Base Database model
|
||||
*
|
||||
* @author Donghuang
|
||||
* @date Jun 18, 2022 09:39:26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@FieldNameConstants(prefix = "")
|
||||
public class BaseDbModel implements Serializable {
|
||||
public class BaseDbModel implements Serializable, BasicEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
@ -25,6 +27,37 @@ public class BaseDbModel implements Serializable {
|
||||
private Date dateCreated;
|
||||
private String updatedBy;
|
||||
private Date dateUpdated;
|
||||
private boolean enabled;
|
||||
private Boolean enabled;
|
||||
private String note;
|
||||
|
||||
/**
|
||||
* convenient method
|
||||
*
|
||||
* @return true if is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled != null && enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* init date created
|
||||
*/
|
||||
@Override
|
||||
public void beforeInsert() {
|
||||
if (dateCreated == null) {
|
||||
dateCreated = new Date();
|
||||
}
|
||||
|
||||
if (enabled == null) {
|
||||
enabled = Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update date updated
|
||||
*/
|
||||
@Override
|
||||
public void beforeUpdate() {
|
||||
dateUpdated = new Date();
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ package com.pudonghot.ambition.crm.model;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.chyxion.tigon.mybatis.Table;
|
||||
import me.chyxion.tigon.mybatis.NotUpdate;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import me.chyxion.tigon.mybatis.UseGeneratedKeys;
|
||||
import me.chyxion.tigon.mybatis.NotUpdateWhenNull;
|
||||
import com.pudonghot.ambition.crm.enumeration.EnumExportTaskStatus;
|
||||
|
||||
/**
|
||||
@ -13,6 +14,7 @@ import com.pudonghot.ambition.crm.enumeration.EnumExportTaskStatus;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@UseGeneratedKeys
|
||||
@Table("crm_export_task")
|
||||
@FieldNameConstants(prefix = "")
|
||||
public class ExportTask extends BaseDbModel {
|
||||
@ -20,6 +22,7 @@ public class ExportTask extends BaseDbModel {
|
||||
|
||||
// Properties
|
||||
private EnumExportTaskStatus status;
|
||||
@NotUpdate
|
||||
@NotUpdateWhenNull
|
||||
private String location;
|
||||
private boolean downloaded;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import me.chyxion.tigon.model.M3;
|
||||
import me.chyxion.tigon.mybatis.Table;
|
||||
import me.chyxion.tigon.mybatis.Transient;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import me.chyxion.tigon.mybatis.UseGeneratedKeys;
|
||||
|
||||
/**
|
||||
* @version 0.0.1
|
||||
@ -16,6 +17,7 @@ import lombok.experimental.FieldNameConstants;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@UseGeneratedKeys
|
||||
@Table("crm_file_info")
|
||||
@FieldNameConstants(prefix = "")
|
||||
public class FileInfo extends M3<String, String> {
|
||||
|
@ -25,7 +25,7 @@ public class User extends M3<String, String> {
|
||||
// roles
|
||||
public static final String ROLE_ADMIN = "ADMIN";
|
||||
public static final String ROLE_LELI = "leli";
|
||||
public static final String ROLE_CHYXION = "donghuang";
|
||||
public static final String ROLE_DONGHUANG = "donghuang";
|
||||
|
||||
// Properties
|
||||
private String account;
|
||||
|
@ -6,16 +6,6 @@ export default Service.extend({
|
||||
ajax: service('ajax'),
|
||||
connect() {
|
||||
const me = this;
|
||||
if (me.get('connected')) {
|
||||
console.info('Websocket is connected, reconnect.');
|
||||
me.disconnect();
|
||||
}
|
||||
|
||||
// connect
|
||||
if (!me.get('stompClient')) {
|
||||
me.set('stompClient', Stomp.over(new SockJS('/stomp')));
|
||||
}
|
||||
|
||||
const user = me.get('ajax.user');
|
||||
|
||||
if (!user) {
|
||||
@ -23,15 +13,57 @@ export default Service.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
me.get('stompClient').connect({}, function() {
|
||||
console.info('Connect websocket.', user);
|
||||
me.stompClient.subscribe('/topic/' + user.id, function(msg) {
|
||||
if (me.get('connected')) {
|
||||
console.info('Websocket is connected, reconnect.');
|
||||
me.disconnect();
|
||||
}
|
||||
|
||||
const client = new StompJs.Client({
|
||||
// brokerURL: 'ws://localhost:15674/ws',
|
||||
webSocketFactory: function () {
|
||||
// Note that the URL is different from the WebSocket URL
|
||||
return new SockJS('/stomp');
|
||||
},
|
||||
connectHeaders: {
|
||||
user: user.id,
|
||||
// passcode: 'password',
|
||||
},
|
||||
debug: function (str) {
|
||||
console.log(str);
|
||||
},
|
||||
reconnectDelay: 5000,
|
||||
heartbeatIncoming: 4000,
|
||||
heartbeatOutgoing: 4000,
|
||||
});
|
||||
|
||||
client.onConnect = function(frame) {
|
||||
// Do something, all subscribes must be done is this callback
|
||||
// This is needed because this will be executed after a (re)connect
|
||||
console.info('Websocket connected: ', frame);
|
||||
me.set('subscription', client.subscribe('/topic/' + user.id, function(msg) {
|
||||
console.info('On websocket message: ', msg);
|
||||
$.trigger('WEBSOCKET', JSON.parse(msg.body));
|
||||
});
|
||||
}));
|
||||
me.set('connected', true);
|
||||
console.info('Websocket connected.');
|
||||
});
|
||||
};
|
||||
|
||||
client.onDisconnect = function(frame) {
|
||||
console.log('On websocket disconnect: ', frame);
|
||||
me.set('connected', false);
|
||||
};
|
||||
|
||||
client.onStompError = function (frame) {
|
||||
// Will be invoked in case of error encountered at Broker
|
||||
// Bad login/passcode typically will cause an error
|
||||
// Complaint brokers will set `message` header with a brief message. Body may contain details.
|
||||
// Compliant brokers will terminate the connection after any error
|
||||
console.log('Broker reported error: ', frame.headers['message']);
|
||||
console.log('Additional details: ', frame.body);
|
||||
me.set('connected', false);
|
||||
};
|
||||
|
||||
client.activate();
|
||||
me.set('client', client);
|
||||
},
|
||||
willDestroy() {
|
||||
const me = this;
|
||||
@ -40,10 +72,8 @@ export default Service.extend({
|
||||
},
|
||||
disconnect() {
|
||||
const me = this;
|
||||
if (me.get('connected') && me.get('stompClient')) {
|
||||
me.get('stompClient').disconnect();
|
||||
me.set('connected', false);
|
||||
console.info('Websocket disconnected.');
|
||||
if (me.get('connected') && me.get('client')) {
|
||||
me.get('client').deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -110,8 +110,9 @@ module.exports = function(defaults) {
|
||||
importVendor(app, 'node_modules/bootstrap-wysiwyg/src/bootstrap-wysiwyg.js');
|
||||
|
||||
// WebSocket
|
||||
importVendor(app, 'node_modules/@stomp/stompjs/bundles/stomp.umd.js');
|
||||
importVendor(app, 'node_modules/sockjs-client/dist/sockjs.js');
|
||||
importVendor(app, 'node_modules/stomp-websocket/lib/stomp.js');
|
||||
// importVendor(app, 'node_modules/stomp-websocket/lib/stomp.js');
|
||||
|
||||
// Bootstrap Toolkit
|
||||
importVendor(app, 'node_modules/responsive-toolkit/dist/bootstrap-toolkit.js');
|
||||
|
@ -70,8 +70,8 @@
|
||||
"webpack": "^5.73.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sockjs-client": "^1.6.1",
|
||||
"stomp-websocket": "^2.3.4-next"
|
||||
"@stomp/stompjs": "^6.1.2",
|
||||
"sockjs-client": "^1.6.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
|
Loading…
x
Reference in New Issue
Block a user