export task complete

This commit is contained in:
Donghuang 2022-06-23 22:32:12 +08:00
parent 126575f0bf
commit 7640c1dab6
13 changed files with 163 additions and 50 deletions

View File

@ -1,18 +1,27 @@
package com.pudonghot.ambition.crm.controller;
import lombok.val;
import java.io.File;
import java.util.Map;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.mybatis.Search;
import org.springframework.http.HttpStatus;
import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.NotNull;
import com.pudonghot.ambition.crm.model.User;
import org.springframework.http.ResponseEntity;
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 com.pudonghot.ambition.crm.util.FileDownloadUtils;
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.RequestParam;
import com.pudonghot.ambition.crm.service.ExportTaskService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.ambition.crm.ws.service.WebSocketService;
@ -43,6 +52,40 @@ public class ExportTaskController
return list(req, new Search());
}
/**
* download file
* @param id id
* @return file
*/
@RequiresRoles(User.ROLE_ADMIN)
@RequestMapping("/download")
public ResponseEntity file(@NotNull @RequestParam("id") final Long id) {
val exportTask = service.find(id);
if (exportTask == null) {
return ResponseEntity.notFound().build();
}
val userId = getUserId();
if (!userId.equals(exportTask.getCreatedBy())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
exportTask.setDownloaded(true);
exportTask.setUpdatedBy(userId);
((ExportTaskService) service).update(exportTask);
val location = exportTask.getLocation();
if (StringUtils.isNotBlank(location)) {
val file = new File(location);
if (file.exists() && file.isFile()) {
((ExportTaskService) service).notifyTaskUpdated(userId);
return FileDownloadUtils.toResponseEntity(file, file.getName());
}
}
return ResponseEntity.notFound().build();
}
/**
* {@inheritDoc}
@ -62,8 +105,7 @@ public class ExportTaskController
@PostMapping("/test-push")
@RequiresRoles(User.ROLE_ADMIN)
public void testPush(@RequestBody ExportTaskUpdatedWsResp resp) {
resp.setEmployeeKey(getUserId());
webSocketService.publish(resp);
public void testPush(@RequestBody Map<String, Object> resp) {
webSocketService.publish(getUserId(), resp);
}
}

View File

@ -1,6 +1,7 @@
package com.pudonghot.ambition.crm.service;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import com.pudonghot.ambition.crm.model.ExportTask;
import me.chyxion.tigon.service2.BaseQueryService;
@ -26,5 +27,12 @@ public interface ExportTaskService extends BaseQueryService<Long, ExportTask> {
* @param employeeKey employee key
*/
void notifyTaskUpdated(@NotBlank String employeeKey);
/**
* update task
*
* @param exportTask task
*/
void update(@NotNull ExportTask exportTask);
}

View File

@ -24,6 +24,7 @@ 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;
import com.pudonghot.ambition.crm.ws.model.ExportTaskCompletedWsResp;
/**
* @author Donghuang
@ -73,15 +74,16 @@ public class ExportTaskServiceSupport
exportTask.setLocation(new File(distDir, exportFile.getName()).getPath());
exportTask.setDateUpdated(new Date());
mapper.update(exportTask);
} catch (final Throwable e) {
notifyTaskComplete(employeeKey, true, null);
}
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);
notifyTaskComplete(employeeKey, false, e.getMessage());
}
notifyTaskUpdated(employeeKey);
}
finally {
LOCK_MAP.remove(employeeKey);
@ -89,6 +91,15 @@ public class ExportTaskServiceSupport
});
}
void notifyTaskComplete(final String employeeKey, final boolean success, final String message) {
val resp = new ExportTaskCompletedWsResp(employeeKey,
mapper.count(new Search(ExportTask.CREATED_BY, employeeKey)
.isFalse(ExportTask.DOWNLOADED)));
resp.setSuccess(success);
resp.setMessage(message);
webSocketService.publish(resp);
}
/**
* {@inheritDoc}
*/
@ -99,4 +110,12 @@ public class ExportTaskServiceSupport
mapper.count(new Search(ExportTask.CREATED_BY, employeeKey)
.isFalse(ExportTask.DOWNLOADED))));
}
/**
* {@inheritDoc}
*/
@Override
public void update(final ExportTask exportTask) {
mapper.update(exportTask);
}
}

View File

@ -10,7 +10,7 @@ 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;
import org.springframework.core.io.FileSystemResource;
/**
* @author Donghuang
@ -36,16 +36,11 @@ public class FileDownloadUtils {
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
try (val fin = new FileInputStream(file)) {
return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(fin));
}
catch (final IOException e) {
log.error("Download file error caused.", e);
}
return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new FileSystemResource(file));
}
return ResponseEntity.notFound().build();

View File

@ -0,0 +1,30 @@
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 ExportTaskCompletedWsResp extends BaseWsResp {
private static final long serialVersionUID = 1L;
private Integer countUndownloaded;
private Boolean success;
private String message;
public ExportTaskCompletedWsResp() {
setType("EXPORT_TASK_COMPLETED");
}
public ExportTaskCompletedWsResp(final String employeeKey, final Integer countUndownloaded) {
this();
this.countUndownloaded = countUndownloaded;
setEmployeeKey(employeeKey);
}
}

View File

@ -15,7 +15,6 @@ public class ExportTaskUpdatedWsResp extends BaseWsResp {
private static final long serialVersionUID = 1L;
private Integer countUndownloaded;
private Integer countFailed;
public ExportTaskUpdatedWsResp() {
setType("EXPORT_TASK_UPDATED");

View File

@ -21,7 +21,7 @@ datasource:
username: root
database:
backup-dir: /Users/chyxion/Workspaces/ambition-crm/database_backups
backup-dir: d:/data/database_backups
restore-shell: /data/program/mysql-backup/bin/mysql_restore.sh
file:

View File

@ -1,5 +1,12 @@
import BaseListRoute from './../base-list';
import { set } from '@ember/object';
export default BaseListRoute.extend({
breadcrumbs: [{text: 'Export Tasks'}]
breadcrumbs: [{text: 'Export Tasks'}],
actions: {
download(rec) {
const me = this;
set(rec, 'downloaded', true);
}
}
});

View File

@ -7,8 +7,20 @@ export default BaseService.extend({
const me = this;
$.on('WEBSOCKET', function(e, data) {
console.log('Export task: On websocket message trigger.', e, data);
if (data.type == 'EXPORT_TASK_UPDATED') {
me.set('countUndownloaded', data ? data.countUndownloaded : null);
if (!data) {
console.log('Event data is null, ignore.', e);
return;
}
me.set('countUndownloaded', data.countUndownloaded);
if (data.type == 'EXPORT_TASK_COMPLETED') {
if (data.success) {
me.get('message').alert('Export task completed.');
}
else {
me.get('message').error('Export task error caused: ' + data.message);
}
}
});
}

View File

@ -11,6 +11,11 @@ export default Service.extend({
warn(msg) {
toastr.warning(msg);
},
error(msg) {
toastr.options.closeButton = true;
toastr.error(msg);
toastr.options.closeButton = false;
},
alert2(msg) {
this._show_alert_(msg);
},

View File

@ -118,7 +118,7 @@ input[type="number"] {
font-size: 10px;
border-radius: 50%;
color: #fff;
background-color: rgba(255, 0, 0, 0.8);
background-color: rgba(209, 91, 71, 0.9);
// rgba(0, 0, 0, 0.7);
display: flex;

View File

@ -100,9 +100,11 @@
</li>
<li>
<LinkTo @route="export-task.list" @model=1>
<i class="menu-icon fa fa-tasks {{if this.exportTaskService.countUndownloaded 'green' 'blue'}}"></i>
<span class="export-task-badge">21</span>
<span class="menu-text"> Export Task {{#if this.exportTaskService.countUndownloaded}}<span class="badge badge-danger">{{this.exportTaskService.countUndownloaded}}</span>{{/if}}</span>
<i class="menu-icon fa fa-tasks blue"></i>
{{#if this.exportTaskService.countUndownloaded}}
<span class="export-task-badge">{{this.exportTaskService.countUndownloaded}}</span>
{{/if}}
<span class="menu-text"> Export Task </span>
</LinkTo>
</li>
{{/if}}

View File

@ -7,7 +7,9 @@
<table class="table table-striped table-bordered table-hover dataTable" style="border: 1px solid #ddd;">
<thead class="thin-border-bottom">
<tr>
{{sortable-th name='dateCreated' text='Date Created' style='min-width: 105px;'}}
<th>
Date Created
</th>
<th>
<i class="ace-icon fa fa-exchange bigger-110"></i>
Status
@ -32,32 +34,24 @@
{{it.status}}
</td>
<td>
{{status-cell model=it field='downloaded' enabledText='DOWNLOADED' disabledText='NOT DOWNLOAD'}}
{{#if it.downloaded}}
<span class="green">
<i class="ace-icon fa fa-check-square bigger-120"></i>
DOWNLOADED
</span>
{{else}}
<span class="blue">
<i class="ace-icon fa fa-spinner bigger-120"></i>
NOT DOWNLOAD
</span>
{{/if}}
</td>
<td>
<div class="hidden-sm hidden-xs btn-group">
{{#link-to 'user.edit' it.id class='btn btn-xs btn-info' data-rel='tooltip' title='Download'}}
<i class="ace-icon fa fa-pencil bigger-120"></i>
<div class="btn-group">
<a {{on 'click' (route-action 'download' it)}} target="_blank" href="/export-task/download?id={{it.id}}" class="btn btn-xs btn-info" data-rel="tooltip" title="Download">
<i class="ace-icon fa fa-download bigger-120"></i>
Download
{{/link-to}}
</div>
<div class="hidden-md hidden-lg">
<div class="inline pos-rel">
<button class="btn btn-minier btn-primary dropdown-toggle" data-toggle="dropdown" data-position="auto">
<i class="ace-icon fa fa-cog icon-only bigger-110"></i>
</button>
<ul class="dropdown-menu dropdown-only-icon dropdown-yellow dropdown-menu-right dropdown-caret dropdown-close">
<li>
{{#link-to 'user.edit' it.id class='tooltip-info' data-rel='tooltip' title='Download'}}
<span class="blue">
<i class="ace-icon fa fa-pencil-square-o bigger-120"></i>
Download
</span>
{{/link-to}}
</li>
</ul>
</div>
</a>
</div>
</td>
</tr>