extract attachment service

This commit is contained in:
Shaun Chyxion 2018-11-24 15:32:40 +08:00
parent 3f00923543
commit 3b291c04fa
27 changed files with 1005 additions and 192 deletions

View File

@ -1,11 +1,10 @@
package com.pudonghot.ambition.crm.auth;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
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;
import com.pudonghot.ambition.crm.model.User;
import org.springframework.stereotype.Service;
import me.chyxion.tigon.shiro.model.Credential;
@ -56,8 +55,20 @@ public class AuthRealmSupport extends AuthRealm {
new String((char[])password));
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> roles(Object principal) {
final User user = userService.find(new Search(User.EMPLOYEE_ID, principal));
return user.isAdmin() ? Arrays.asList(User.ROLE_ADMIN) : Collections.emptyList();
final List<String> roles = new ArrayList<>(4);
if (user.isAdmin()) {
roles.add(User.ROLE_ADMIN);
}
final String account = user.getAccount();
if (ArrayUtils.contains(new String[] {User.ROLE_LELI, User.ROLE_CHYXION}, account)) {
roles.add(account);
}
return roles;
}
}

View File

@ -25,8 +25,8 @@ import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationAttachmentFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationAttachmentFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationAttachedFileFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationAttachedFileFormForUpdate;
/**
* @author Shaun Chyxion <br>
@ -41,8 +41,8 @@ public class ApplicationController
@Autowired
private UserService userService;
private final List<String> SEARCH_COLS =
Arrays.asList(CustomerProperty.NAME,
CustomerProperty.NOTE);
Arrays.asList(Application.NAME,
Application.NOTE);
@RequestMapping("/list")
public ListResult<ViewModel<Application>> list(
@ -132,7 +132,7 @@ public class ApplicationController
@RequestMapping(value = "/add-attachment", method = RequestMethod.POST)
public ViewModel<AttachedFile> addAttachment(
@Valid ApplicationAttachmentFormForCreate form) {
@Valid ApplicationAttachedFileFormForCreate form) {
Assert.state(!form.getAttachment().isEmpty(), "Image content is empty");
form.setAdmin(getAuthUser().getUser().getData().isAdmin());
return new ViewModel<>(((ApplicationService) queryService).addAttachment(form));
@ -145,7 +145,7 @@ public class ApplicationController
}
@RequestMapping(value = "/update-attachment", method = RequestMethod.POST)
public void updateImage(@Valid ApplicationAttachmentFormForUpdate form) {
public void updateImage(@Valid ApplicationAttachedFileFormForUpdate form) {
form.setAdmin(getAuthUser().getUser().getData().isAdmin());
((ApplicationService) queryService).updateAttachment(form);
}

View File

@ -0,0 +1,162 @@
package com.pudonghot.ambition.crm.controller;
import java.util.List;
import java.util.Arrays;
import javax.validation.Valid;
import org.springframework.util.Assert;
import me.chyxion.tigon.mybatis.Search;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.model.ListResult;
import com.pudonghot.ambition.crm.model.*;
import org.apache.commons.lang3.tuple.Pair;
import javax.validation.constraints.NotBlank;
import org.springframework.stereotype.Controller;
import org.apache.shiro.authz.annotation.Logical;
import com.pudonghot.ambition.crm.service.UserService;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMethod;
import com.pudonghot.ambition.crm.service.LocalProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pudonghot.ambition.crm.form.create.LocalProductFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductFormForUpdate;
import com.pudonghot.ambition.crm.form.create.LocalProductImageFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductImageFormForUpdate;
import com.pudonghot.ambition.crm.form.create.LocalProductAttachedFileFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductAttachedFileFormForUpdate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Jun 23, 2017 21:39:49
*/
@Controller
@RequestMapping("/local-product")
public class LocalProductController
extends BaseQueryController<LocalProduct> {
@Autowired
private UserService userService;
private final List<String> SEARCH_COLS =
Arrays.asList(CustomerProperty.NAME,
CustomerProperty.NOTE);
@RequestMapping("/list")
public ListResult<ViewModel<LocalProduct>> list(
@Min(0)
@RequestParam(value = "start", defaultValue = "0")
final int start,
@Min(1)
@Max(2048)
@RequestParam(value = "limit", defaultValue = "16")
final int limit,
@RequestParam(value = "filters", required = false)
final String filters,
@RequestParam(value = "search", required = false)
final String strSearch) {
final ListResult<ViewModel<LocalProduct>> result =
listViewModels(new Search().asc(LocalProduct.NAME),
start, limit, strSearch, null, filters, null);
result.setAttr("namePrefixes",
((LocalProductService) queryService).listNamePrefixes());
return result;
}
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION}, 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}, logical = Logical.OR)
@RequestMapping(value = "/update", method = RequestMethod.POST)
public void update(
@Valid LocalProductFormForUpdate form) {
((LocalProductService) queryService).update(form);
}
/**
* {@inheritDoc}
*/
@Override
public ViewModel<LocalProduct> find(final String id) {
final ViewModel<LocalProduct> viewModel = queryService.findViewModel(id);
if (getAuthUser().getUser().getData().isAdmin()) {
viewModel.setAttr("users", userService.listViewModels(
new Search().asc(User.EMPLOYEE_ID)));
}
return viewModel;
}
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION}, logical = Logical.OR)
@RequestMapping(value = "/add-image", method = RequestMethod.POST)
public ViewModel<AttachedImage> addImage(
@Valid LocalProductImageFormForCreate form) {
Assert.state(!form.getImage().isEmpty(), "Image content is empty");
return new ViewModel<>(((LocalProductService) queryService).addImage(form));
}
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION}, 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}, 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}, logical = Logical.OR)
@RequestMapping(value = "/add-attachment", method = RequestMethod.POST)
public ViewModel<AttachedFile> addAttachment(
@Valid LocalProductAttachedFileFormForCreate form) {
Assert.state(!form.getAttachment().isEmpty(), "Image content is empty");
return new ViewModel<>(((LocalProductService) queryService).addAttachment(form));
}
@RequestMapping(value = "/remove-attachment", method = RequestMethod.POST)
public void removeAttachment(@NotBlank @RequestParam("id") String id) {
((LocalProductService) queryService).removeAttachment(id);
}
@RequiresRoles(value = {User.ROLE_LELI, User.ROLE_CHYXION}, 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}, logical = Logical.OR)
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public void delete(@NotBlank @RequestParam("id") String id) {
((LocalProductService) queryService).delete(id);
}
/**
* {@inheritDoc}
*/
@Override
protected List<String> searchCols() {
return SEARCH_COLS;
}
/**
* {@inheritDoc}
*/
@Override
protected Pair<String, Class<?>> filterCol(final String field) {
if (LocalProduct.OWNER.equals(field)) {
return Pair.of(field, String.class);
}
if (LocalProduct.NAME_PREFIX.equals(field)) {
return Pair.of(field, String.class);
}
return null;
}
}

View File

@ -12,8 +12,8 @@ import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationAttachmentFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationAttachmentFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationAttachedFileFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationAttachedFileFormForUpdate;
/**
* @author Shaun Chyxion <br>
@ -43,12 +43,11 @@ public interface ApplicationService
*/
void updateImage(@Valid ApplicationImageFormForUpdate form);
/**
* add attachment
* @param form form
*/
AttachedFile addAttachment(@Valid ApplicationAttachmentFormForCreate form);
AttachedFile addAttachment(@Valid ApplicationAttachedFileFormForCreate form);
/**
* remove attachment
@ -62,7 +61,7 @@ public interface ApplicationService
* update attachment
* @param form form
*/
void updateAttachment(@Valid ApplicationAttachmentFormForUpdate form);
void updateAttachment(@Valid ApplicationAttachedFileFormForUpdate form);
/**
* list name prefixes

View File

@ -0,0 +1,47 @@
package com.pudonghot.ambition.crm.service;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import com.pudonghot.ambition.crm.model.Attachment;
import me.chyxion.tigon.model.M2;
import me.chyxion.tigon.mybatis.BaseMapper;
import org.springframework.web.multipart.MultipartFile;
import com.pudonghot.ambition.crm.form.update.AttachmentFormForUpdate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Jun 23, 2017 21:35:24
*/
public interface AttachmentService {
<T extends Attachment> List<T> upload(
String ownerId,
int sort,
MultipartFile[] files,
String[] titles,
String createdBy,
Supplier<T> constructor,
Consumer<T> insert);
<T extends Attachment> void update(
AttachmentFormForUpdate form,
Function<String, T> finder,
Function<String, List<T>> listSort,
Consumer<T> updater);
<T extends Attachment> void remove(
String id,
Function<String, T> finder,
Function<String, Integer> deleter,
Function<String, Integer> sortUpdater,
Consumer<T> validator);
<Key, M extends M2<Key>> M deleteOwner(
Key id,
BaseMapper<Key, M> mapper,
Consumer<M> validator);
}

View File

@ -0,0 +1,68 @@
package com.pudonghot.ambition.crm.service;
import java.util.Map;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import com.pudonghot.ambition.crm.model.LocalProduct;
import com.pudonghot.ambition.crm.model.AttachedFile;
import com.pudonghot.ambition.crm.model.AttachedImage;
import me.chyxion.tigon.service.BaseCrudByFormService;
import com.pudonghot.ambition.crm.form.create.LocalProductFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductFormForUpdate;
import com.pudonghot.ambition.crm.form.create.LocalProductImageFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductImageFormForUpdate;
import com.pudonghot.ambition.crm.form.create.LocalProductAttachedFileFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductAttachedFileFormForUpdate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Jun 23, 2017 21:35:24
*/
public interface LocalProductService
extends BaseCrudByFormService<String, LocalProduct, LocalProductFormForCreate, LocalProductFormForUpdate> {
/**
* add image
* @param form form
*/
AttachedImage addImage(@Valid LocalProductImageFormForCreate form);
/**
* remove image
* @param id image id
*/
void removeImage(@NotBlank String id);
/**
* update image
* @param form form
*/
void updateImage(@Valid LocalProductImageFormForUpdate form);
/**
* add attachment
* @param form form
*/
AttachedFile addAttachment(@Valid LocalProductAttachedFileFormForCreate form);
/**
* remove attachment
* @param id attachment id
*/
void removeAttachment(@NotBlank String id);
/**
* update attachment
* @param form form
*/
void updateAttachment(@Valid LocalProductAttachedFileFormForUpdate form);
/**
* list name prefixes
* @return name prefixes
*/
List<Map<String, String>> listNamePrefixes();
}

View File

@ -3,24 +3,19 @@ package com.pudonghot.ambition.crm.service.support;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.io.IOException;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import me.chyxion.tigon.mybatis.Search;
import org.springframework.util.Assert;
import me.chyxion.tigon.model.ViewModel;
import com.pudonghot.ambition.crm.model.*;
import org.apache.commons.io.FilenameUtils;
import com.pudonghot.ambition.crm.mapper.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.pudonghot.ambition.file.DiskFileApi;
import com.pudonghot.ambition.crm.service.UserService;
import org.springframework.web.multipart.MultipartFile;
import com.pudonghot.ambition.crm.service.AttachmentService;
import com.pudonghot.ambition.crm.service.ApplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import me.chyxion.tigon.service.support.BaseCrudServiceSupport;
@ -28,11 +23,10 @@ import org.springframework.transaction.annotation.Transactional;
import com.pudonghot.ambition.crm.service.CustomerPermissionService;
import com.pudonghot.ambition.crm.form.update.ApplicationFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationFileFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationImageFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationImageFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationAttachmentFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationAttachmentFormForUpdate;
import com.pudonghot.ambition.crm.form.create.ApplicationAttachedFileFormForCreate;
import com.pudonghot.ambition.crm.form.update.ApplicationAttachedFileFormForUpdate;
/**
* @author Shaun Chyxion <br>
@ -59,6 +53,8 @@ public class ApplicationServiceSupport
private UserService userService;
@Autowired
private CustomerPermissionService customerPermissionService;
@Autowired
private AttachmentService attachmentService;
/**
* {@inheritDoc}
@ -72,8 +68,8 @@ public class ApplicationServiceSupport
final Date now = new Date();
application.setDateUpdated(now);
uploadFiles(id, 1, form.getImages(), form.getImageTitles(), form.getCreatedBy(), AttachedImage::new, imageMapper::insert);
uploadFiles(id, 1, form.getAttachments(), form.getAttachmentTitles(), form.getCreatedBy(), AttachedFile::new, attachmentMapper::insert);
attachmentService.upload(id, 1, form.getImages(), form.getImageTitles(), form.getCreatedBy(), AttachedImage::new, imageMapper::insert);
attachmentService.upload(id, 1, form.getAttachments(), form.getAttachmentTitles(), form.getCreatedBy(), AttachedFile::new, attachmentMapper::insert);
mapper.insert(application);
return toViewModel(application);
}
@ -146,7 +142,7 @@ public class ApplicationServiceSupport
final String applicationId = form.getApplicationId();
final String createdBy = form.getCreatedBy();
validatePerm(applicationId, createdBy, form.isAdmin());
return uploadFiles(applicationId,
return attachmentService.upload(applicationId,
imageMapper.nextSort(applicationId),
new MultipartFile[] {form.getImage()},
new String[] {form.getNote()},
@ -160,7 +156,7 @@ public class ApplicationServiceSupport
*/
@Override
public void updateImage(final ApplicationImageFormForUpdate form) {
updateFile(form, imageMapper::find, imageMapper::listSort, imageMapper::update);
attachmentService.update(form, imageMapper::find, imageMapper::listSort, imageMapper::update);
}
/**
@ -169,18 +165,22 @@ public class ApplicationServiceSupport
@Override
@Transactional(rollbackFor = Throwable.class)
public void removeImage(final String id, final String userId, final boolean admin) {
removeFile(id, userId, admin, imageMapper::find, imageMapper::delete, imageMapper::updateSort);
attachmentService.remove(id,
imageMapper::find,
imageMapper::delete,
imageMapper::updateSort,
attachment -> validatePerm(attachment.getOwnerId(), userId, admin));
}
/**
* {@inheritDoc}
*/
@Override
public AttachedFile addAttachment(final ApplicationAttachmentFormForCreate form) {
public AttachedFile addAttachment(final ApplicationAttachedFileFormForCreate form) {
final String applicationId = form.getApplicationId();
final String createdBy = form.getCreatedBy();
validatePerm(applicationId, createdBy, form.isAdmin());
return uploadFiles(applicationId,
return attachmentService.upload(applicationId,
attachmentMapper.nextSort(applicationId),
new MultipartFile[] {form.getAttachment()},
new String[] {form.getNote()},
@ -193,8 +193,8 @@ public class ApplicationServiceSupport
* {@inheritDoc}
*/
@Override
public void updateAttachment(final ApplicationAttachmentFormForUpdate form) {
updateFile(form, attachmentMapper::find, attachmentMapper::listSort, attachmentMapper::update);
public void updateAttachment(final ApplicationAttachedFileFormForUpdate form) {
attachmentService.update(form, attachmentMapper::find, attachmentMapper::listSort, attachmentMapper::update);
}
/**
@ -211,7 +211,11 @@ public class ApplicationServiceSupport
@Override
@Transactional(rollbackFor = Throwable.class)
public void removeAttachment(final String id, final String userId, final boolean admin) {
removeFile(id, userId, admin, attachmentMapper::find, attachmentMapper::delete, attachmentMapper::updateSort);
attachmentService.remove(id,
attachmentMapper::find,
attachmentMapper::delete,
attachmentMapper::updateSort,
attachment -> validatePerm(attachment.getOwnerId(), userId, admin));
}
/**
@ -220,130 +224,11 @@ public class ApplicationServiceSupport
@Override
@Transactional(rollbackFor = Throwable.class)
public Application delete(final String id) {
log.info("Delete application [{}].", id);
final Application app = find(id);
Assert.state(app != null, "No application [" + id + "] found");
Assert.state(!app.isEnabled(), "Application [" + id + "] is enabled");
Assert.state(customerApplicationMapper.count(
new Search(CustomerApplication.APPLICATION_ID, id)) == 0,
"Application [" + id + "] is in using");
final Search appFileSearch = new Search(AttachedImage.OWNER_ID, id);
imageMapper.list(appFileSearch).forEach(
image -> fileApi.deleteById(image.getFileId()));
imageMapper.delete(appFileSearch);
attachmentMapper.list(appFileSearch).forEach(
attachment -> fileApi.deleteById(attachment.getFileId()));
attachmentMapper.delete(appFileSearch);
mapper.delete(id);
return app;
}
private <T extends Attachment> List<T> uploadFiles(
final String applicationId,
int sort,
final MultipartFile[] files,
final String[] titles,
final String createdBy,
Supplier<T> constructor,
Consumer<T> insert) {
final List<T> appFiles = new ArrayList<>();
final Date now = new Date();
final String fileFolder = fileFolder(applicationId);
int i = 0;
for (final MultipartFile file : files) {
if (!file.isEmpty()) {
final String originalFilename = file.getOriginalFilename();
try (final InputStream ins = file.getInputStream()) {
final String fileId = idSeq.get();
final T appFile = constructor.get();
final FileInfo fileInfo = fileApi.upload(ins,
file.getSize(),
fileFolder,
fileId,
file.getContentType(),
FilenameUtils.getExtension(originalFilename),
originalFilename);
appFile.setId(fileId);
appFile.setOwnerId(applicationId);
appFile.setFileId(fileInfo.getId());
appFile.setUrl(fileInfo.getUrl());
appFile.setSort(sort++);
appFile.setEnabled(true);
appFile.setNote(StringUtils.defaultIfBlank(titles[i], originalFilename));
appFile.setCreatedBy(createdBy);
appFile.setDateCreated(now);
// insert
insert.accept(appFile);
appFiles.add(appFile);
}
catch (final IOException e) {
throw new IllegalStateException(
"Read upload file [" + originalFilename + "] error cased", e);
}
}
else {
log.info("Application file [{}] is empty, ignore.", file);
}
++i;
}
return appFiles;
}
private <T extends Attachment> void updateFile(
final ApplicationFileFormForUpdate form,
final Function<String, T> finder,
final Function<String, List<T>> listSort,
final Consumer<T> updater) {
final String id = form.getId();
final T appImage = finder.apply(id);
Assert.state(appImage != null, "No application file [" + id + "] found");
final String applicationId = appImage.getOwnerId();
final String updatedBy = form.getUpdatedBy();
validatePerm(applicationId, updatedBy, form.isAdmin());
boolean sortUpdated = false;
final float sort = form.getSort();
final float sortOld = appImage.getSort();
if (sort < sortOld) {
sortUpdated = true;
appImage.setSort(sortOld - 1.5f);
}
else if (sort > sortOld) {
sortUpdated = true;
appImage.setSort(sortOld + 1.5f);
}
appImage.setNote(form.getNote());
appImage.setDateUpdated(new Date());
appImage.setUpdatedBy(updatedBy);
updater.accept(appImage);
if (sortUpdated) {
listSort.apply(applicationId).forEach(updater);
}
}
private <T extends Attachment> void removeFile(
final String id,
final String userId,
final boolean admin,
final Function<String, T> finder,
final Function<String, Integer> deleter,
final Function<String, Integer> sortUpdater) {
final T appFile = finder.apply(id);
Assert.state(appFile != null, "No application file [" + id + "] found");
final String applicationId = appFile.getOwnerId();
validatePerm(applicationId, userId, admin);
fileApi.delete(fileFolder(applicationId) + "/" + id);
deleter.apply(id);
sortUpdater.apply(applicationId);
}
private String fileFolder(final String appId) {
return "app/" + appId;
return attachmentService.deleteOwner(id,
mapper, application ->
Assert.state(customerApplicationMapper.count(
new Search(CustomerApplication.APPLICATION_ID, id)) == 0,
"Application [" + id + "] is in using"));
}
private Application validatePerm(final String appId, final String userId, final boolean admin) {

View File

@ -0,0 +1,196 @@
package com.pudonghot.ambition.crm.service.support;
import java.util.Date;
import java.util.List;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.model.M2;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import me.chyxion.tigon.mybatis.Search;
import me.chyxion.tigon.sequence.IdSequence;
import org.springframework.util.Assert;
import me.chyxion.tigon.mybatis.BaseMapper;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.pudonghot.ambition.file.DiskFileApi;
import com.pudonghot.ambition.crm.model.FileInfo;
import com.pudonghot.ambition.crm.model.Attachment;
import com.pudonghot.ambition.crm.model.AttachedImage;
import org.springframework.web.multipart.MultipartFile;
import com.pudonghot.ambition.crm.service.AttachmentService;
import com.pudonghot.ambition.crm.mapper.AttachedFileMapper;
import com.pudonghot.ambition.crm.mapper.AttachedImageMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.pudonghot.ambition.crm.form.update.AttachmentFormForUpdate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Jun 23, 2017 21:36:31
*/
@Slf4j
@Service
public class AttachmentServiceSupport
implements AttachmentService {
@Autowired
private IdSequence idSeq;
@Autowired
private AttachedImageMapper imageMapper;
@Autowired
private AttachedFileMapper attachmentMapper;
@Autowired
private DiskFileApi fileApi;
/**
* {@inheritDoc}
*/
public <T extends Attachment> List<T> upload(
final String ownerId,
int sort,
final MultipartFile[] files,
final String[] titles,
final String createdBy,
final Supplier<T> constructor,
final Consumer<T> insert) {
final List<T> appFiles = new ArrayList<>();
final Date now = new Date();
final String fileFolder = fileFolder(ownerId);
int i = 0;
for (final MultipartFile file : files) {
if (!file.isEmpty()) {
final String originalFilename = file.getOriginalFilename();
try (final InputStream ins = file.getInputStream()) {
final String fileId = idSeq.get();
final T appFile = constructor.get();
final FileInfo fileInfo = fileApi.upload(ins,
file.getSize(),
fileFolder,
fileId,
file.getContentType(),
FilenameUtils.getExtension(originalFilename),
originalFilename);
appFile.setId(fileId);
appFile.setOwnerId(ownerId);
appFile.setFileId(fileInfo.getId());
appFile.setUrl(fileInfo.getUrl());
appFile.setSort(sort++);
appFile.setEnabled(true);
appFile.setNote(StringUtils.defaultIfBlank(titles[i], originalFilename));
appFile.setCreatedBy(createdBy);
appFile.setDateCreated(now);
// insert
insert.accept(appFile);
appFiles.add(appFile);
}
catch (final IOException e) {
throw new IllegalStateException(
"Read upload file [" + originalFilename + "] error cased", e);
}
}
else {
log.info("LocalProduct file [{}] is empty, ignore.", file);
}
++i;
}
return appFiles;
}
/**
* {@inheritDoc}
*/
public <T extends Attachment> void update(
final AttachmentFormForUpdate form,
final Function<String, T> finder,
final Function<String, List<T>> listSort,
final Consumer<T> updater) {
final String id = form.getId();
final T appImage = finder.apply(id);
Assert.state(appImage != null, "No local product file [" + id + "] found");
final String ownerId = appImage.getOwnerId();
final String updatedBy = form.getUpdatedBy();
boolean sortUpdated = false;
final float sort = form.getSort();
final float sortOld = appImage.getSort();
if (sort < sortOld) {
sortUpdated = true;
appImage.setSort(sortOld - 1.5f);
}
else if (sort > sortOld) {
sortUpdated = true;
appImage.setSort(sortOld + 1.5f);
}
appImage.setNote(form.getNote());
appImage.setDateUpdated(new Date());
appImage.setUpdatedBy(updatedBy);
updater.accept(appImage);
if (sortUpdated) {
listSort.apply(ownerId).forEach(updater);
}
}
/**
* {@inheritDoc}
*/
@Transactional(rollbackFor = Throwable.class)
public <T extends Attachment> void remove(
final String id,
final Function<String, T> finder,
final Function<String, Integer> deleter,
final Function<String, Integer> sortUpdater,
final Consumer<T> validator) {
final T attachment = finder.apply(id);
Assert.state(attachment != null, "No file [" + id + "] found");
if (validator != null) {
validator.accept(attachment);
}
final String ownerId = attachment.getOwnerId();
fileApi.delete(fileFolder(ownerId) + "/" + id);
deleter.apply(id);
sortUpdater.apply(ownerId);
}
/**
* {@inheritDoc}
*/
@Transactional(rollbackFor = Throwable.class)
public <Key, M extends M2<Key>> M deleteOwner(final Key ownerId,
final BaseMapper<Key, M> mapper,
final Consumer<M> validator) {
log.info("Delete attachment owner [{}].", ownerId);
final M owner = mapper.find(ownerId);
Assert.state(owner != null, "No attachment owner [" + ownerId + "] found");
Assert.state(!owner.isEnabled(), "Attachment owner [" + ownerId + "] is enabled");
if (validator != null) {
validator.accept(owner);
}
final Search search = new Search(AttachedImage.OWNER_ID, ownerId);
imageMapper.list(search).forEach(
image -> fileApi.deleteById(image.getFileId()));
imageMapper.delete(search);
attachmentMapper.list(search).forEach(
attachment -> fileApi.deleteById(attachment.getFileId()));
attachmentMapper.delete(search);
mapper.delete(ownerId);
return owner;
}
private String fileFolder(final String appId) {
return "app/" + appId;
}
}

View File

@ -0,0 +1,180 @@
package com.pudonghot.ambition.crm.service.support;
import java.util.Map;
import java.util.Date;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import me.chyxion.tigon.mybatis.Search;
import org.springframework.util.Assert;
import me.chyxion.tigon.model.ViewModel;
import com.pudonghot.ambition.crm.model.*;
import com.pudonghot.ambition.crm.mapper.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.pudonghot.ambition.crm.service.AttachmentService;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.ambition.crm.service.LocalProductService;
import me.chyxion.tigon.service.support.BaseCrudServiceSupport;
import org.springframework.transaction.annotation.Transactional;
import com.pudonghot.ambition.crm.form.create.LocalProductFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductFormForUpdate;
import com.pudonghot.ambition.crm.form.create.LocalProductImageFormForCreate;
import com.pudonghot.ambition.crm.form.update.LocalProductImageFormForUpdate;
import com.pudonghot.ambition.crm.form.update.LocalProductAttachedFileFormForUpdate;
import com.pudonghot.ambition.crm.form.create.LocalProductAttachedFileFormForCreate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Jun 23, 2017 21:36:31
*/
@Slf4j
@Service
public class LocalProductServiceSupport
extends BaseCrudServiceSupport<String, LocalProduct, LocalProductMapper>
implements LocalProductService {
@Autowired
private AttachedImageMapper imageMapper;
@Autowired
private AttachedFileMapper attachmentMapper;
@Autowired
private AttachmentService attachmentService;
/**
* {@inheritDoc}
*/
@Override
public ViewModel<LocalProduct> create(final LocalProductFormForCreate form) {
final String id = idSeq.get();
final LocalProduct product = form.copy(new LocalProduct());
product.setId(id);
product.setNamePrefix(namePrefix(product.getName()));
final Date now = new Date();
product.setDateUpdated(now);
attachmentService.upload(id, 1, form.getImages(), form.getImageTitles(), form.getCreatedBy(), AttachedImage::new, imageMapper::insert);
attachmentService.upload(id, 1, form.getAttachments(), form.getAttachmentTitles(), form.getCreatedBy(), AttachedFile::new, attachmentMapper::insert);
mapper.insert(product);
return toViewModel(product);
}
/**
* {@inheritDoc}
*/
@Override
public ViewModel<LocalProduct> update(final LocalProductFormForUpdate form) {
final LocalProduct product = find(form.getId());
Assert.state(product != null, "No local product found");
form.copy(product);
product.setNamePrefix(namePrefix(product.getName()));
return update(product);
}
/**
* {@inheritDoc}
*/
@Override
public ViewModel<LocalProduct> findViewModel(final String id) {
final Search search =
new Search(AttachedImage.OWNER_ID, id)
.asc(AttachedImage.SORT);
return super.findViewModel(id)
.setAttr("images", imageMapper.list(search))
.setAttr("attachments", attachmentMapper.list(search));
}
/**
* {@inheritDoc}
*/
@Override
public AttachedImage addImage(final LocalProductImageFormForCreate form) {
final String productId = form.getProductId();
final String createdBy = form.getCreatedBy();
return attachmentService.upload(productId,
imageMapper.nextSort(productId),
new MultipartFile[] {form.getImage()},
new String[] {form.getNote()},
createdBy,
AttachedImage::new,
imageMapper::insert).iterator().next();
}
/**
* {@inheritDoc}
*/
@Override
public void updateImage(final LocalProductImageFormForUpdate form) {
attachmentService.update(form, imageMapper::find, imageMapper::listSort, imageMapper::update);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional(rollbackFor = Throwable.class)
public void removeImage(final String id) {
attachmentService.remove(id, imageMapper::find, imageMapper::delete, imageMapper::updateSort, null);
}
/**
* {@inheritDoc}
*/
@Override
public AttachedFile addAttachment(final LocalProductAttachedFileFormForCreate form) {
final String productId = form.getProductId();
final String createdBy = form.getCreatedBy();
return attachmentService.upload(productId,
attachmentMapper.nextSort(productId),
new MultipartFile[] {form.getAttachment()},
new String[] {form.getNote()},
createdBy,
AttachedFile::new,
attachmentMapper::insert).iterator().next();
}
/**
* {@inheritDoc}
*/
@Override
public void updateAttachment(final LocalProductAttachedFileFormForUpdate form) {
attachmentService.update(form, attachmentMapper::find, attachmentMapper::listSort, attachmentMapper::update);
}
/**
* {@inheritDoc}
*/
@Override
public List<Map<String, String>> listNamePrefixes() {
return mapper.listNamePrefixes();
}
/**
* {@inheritDoc}
*/
@Override
public void removeAttachment(final String id) {
attachmentService.remove(id,
attachmentMapper::find,
attachmentMapper::delete,
attachmentMapper::updateSort, null);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional(rollbackFor = Throwable.class)
public LocalProduct delete(final String id) {
return attachmentService.deleteOwner(id, mapper, null);
}
private String namePrefix(final String name) {
int i = name.indexOf("-");
if (i > 0) {
return name.substring(0, i);
}
return null;
}
}

View File

@ -12,31 +12,7 @@
<mapper namespace="com.pudonghot.ambition.crm.mapper.ApplicationMapper">
<select id="list" resultType="com.pudonghot.ambition.crm.model.Application">
select
<include refid="cols" />,
(select group_concat(url order by sort separator 0x1d) from
crm_attached_image
where owner_id = a.id
group by owner_id) images,
(select group_concat(note order by sort separator 0x1d) from
crm_attached_image
where owner_id = a.id
group by owner_id) image_titles,
(select group_concat(url order by sort separator 0x1d) from
crm_attached_file
where owner_id = a.id
group by owner_id) attachments,
(select group_concat(note order by sort separator 0x1d) from
crm_attached_file
where owner_id = a.id
group by owner_id) attachment_titles
from
<include refid="table" /> a
<include refid="Tigon.search" />
<include refid="com.pudonghot.ambition.crm.mapper.AttachmentMapper.listForOwner" />
</select>
<select id="listNamePrefixes" resultType="map">

View File

@ -11,6 +11,34 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pudonghot.ambition.crm.mapper.AttachmentMapper">
<sql id="listForOwner">
select
<include refid="cols" />,
(select group_concat(url order by sort separator 0x1d) from
crm_attached_image
where owner_id = a.id
group by owner_id) images,
(select group_concat(note order by sort separator 0x1d) from
crm_attached_image
where owner_id = a.id
group by owner_id) image_titles,
(select group_concat(url order by sort separator 0x1d) from
crm_attached_file
where owner_id = a.id
group by owner_id) attachments,
(select group_concat(note order by sort separator 0x1d) from
crm_attached_file
where owner_id = a.id
group by owner_id) attachment_titles
from
<include refid="table" /> a
<include refid="Tigon.search" />
</sql>
<sql id="nextSort">
select if (owner_id, max(sort) + 1, 1)
from <include refid="table" />

View File

@ -0,0 +1,20 @@
package com.pudonghot.ambition.crm.mapper;
import java.util.List;
import java.util.Map;
import me.chyxion.tigon.mybatis.BaseMapper;
import com.pudonghot.ambition.crm.model.LocalProduct;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Mar 11, 2018 11:39:28
*/
public interface LocalProductMapper extends BaseMapper<String, LocalProduct> {
/**
* list name prefixes
* @return name prefixes
*/
List<Map<String, String>> listNamePrefixes();
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Mar 06, 2018 22:49:18
*/
-->
<!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.LocalProductMapper">
<select id="list" resultType="com.pudonghot.ambition.crm.model.Application">
<include refid="com.pudonghot.ambition.crm.mapper.AttachmentMapper.listForOwner" />
</select>
<select id="listNamePrefixes" resultType="map">
select
distinct name_prefix text, name_prefix value
from <include refid="table" />
where name_prefix is not null
order by name_prefix
</select>
</mapper>

View File

@ -14,7 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
*/
@Getter
@Setter
public class ApplicationAttachmentFormForCreate extends FC2<String> {
public class ApplicationAttachedFileFormForCreate extends FC2<String> {
private static final long serialVersionUID = 1L;
// Properties

View File

@ -0,0 +1,26 @@
package com.pudonghot.ambition.crm.form.create;
import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.form.FC2;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* May 17, 2018 17:12:56
*/
@Getter
@Setter
public class LocalProductAttachedFileFormForCreate extends FC2<String> {
private static final long serialVersionUID = 1L;
// Properties
@NotBlank
private String productId;
@NotNull
private MultipartFile attachment;
}

View File

@ -0,0 +1,32 @@
package com.pudonghot.ambition.crm.form.create;
import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.form.FC2;
import javax.validation.constraints.NotBlank;
import me.chyxion.tigon.format.annotation.Trim;
import me.chyxion.tigon.format.annotation.EmptyToNull;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Mar 11, 2018 11:14:07
*/
@Getter
@Setter
public class LocalProductFormForCreate extends FC2<String> {
private static final long serialVersionUID = 1L;
// Properties
@Trim
@NotBlank
private String name;
@Trim
@EmptyToNull
private String content;
private String[] imageTitles;
private MultipartFile[] images;
private String[] attachmentTitles;
private MultipartFile[] attachments;
}

View File

@ -0,0 +1,25 @@
package com.pudonghot.ambition.crm.form.create;
import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.form.FC2;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.NotBlank;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Mar 11, 2018 11:14:07
*/
@Getter
@Setter
public class LocalProductImageFormForCreate extends FC2<String> {
private static final long serialVersionUID = 1L;
// Properties
@NotBlank
private String productId;
@NotNull
private MultipartFile image;
}

View File

@ -0,0 +1,15 @@
package com.pudonghot.ambition.crm.form.update;
import lombok.Getter;
import lombok.Setter;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* May 17, 2018 17:12:46
*/
@Getter
@Setter
public class ApplicationAttachedFileFormForUpdate extends ApplicationAttachmentFormForUpdate {
private static final long serialVersionUID = 1L;
}

View File

@ -2,14 +2,18 @@ package com.pudonghot.ambition.crm.form.update;
import lombok.Getter;
import lombok.Setter;
import com.pudonghot.ambition.crm.model.Attachment;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* May 17, 2018 17:12:46
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* May 17, 2018 17:50:22
*/
@Getter
@Setter
public class ApplicationAttachmentFormForUpdate extends ApplicationFileFormForUpdate {
public class ApplicationAttachmentFormForUpdate extends AttachmentFormForUpdate {
private static final long serialVersionUID = 1L;
// current user is admin
private boolean admin;
}

View File

@ -12,6 +12,6 @@ import lombok.Setter;
*/
@Getter
@Setter
public class ApplicationImageFormForUpdate extends ApplicationFileFormForUpdate {
public class ApplicationImageFormForUpdate extends ApplicationAttachmentFormForUpdate {
private static final long serialVersionUID = 1L;
}

View File

@ -11,10 +11,8 @@ import me.chyxion.tigon.form.FU2;
*/
@Getter
@Setter
public class ApplicationFileFormForUpdate extends FU2<String, String> {
public class AttachmentFormForUpdate extends FU2<String, String> {
private static final long serialVersionUID = 1L;
// current user is admin
private boolean admin;
private float sort;
}

View File

@ -0,0 +1,15 @@
package com.pudonghot.ambition.crm.form.update;
import lombok.Getter;
import lombok.Setter;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* May 17, 2018 17:12:46
*/
@Getter
@Setter
public class LocalProductAttachedFileFormForUpdate extends LocalProductAttachmentFormForUpdate {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,15 @@
package com.pudonghot.ambition.crm.form.update;
import lombok.Getter;
import lombok.Setter;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* May 17, 2018 17:50:22
*/
@Getter
@Setter
public class LocalProductAttachmentFormForUpdate extends AttachmentFormForUpdate {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,29 @@
package com.pudonghot.ambition.crm.form.update;
import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.form.FU2;
import javax.validation.constraints.NotBlank;
import me.chyxion.tigon.format.annotation.Trim;
import org.hibernate.validator.constraints.Length;
import me.chyxion.tigon.format.annotation.EmptyToNull;
/**
* @version 0.0.1
* @since 0.0.1
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* May 10, 2016 1:42:01 PM
*/
@Getter
@Setter
public class LocalProductFormForUpdate extends FU2<String, String> {
private static final long serialVersionUID = 1L;
@NotBlank
@Length(max = 64)
private String name;
@Trim
@EmptyToNull
private String content;
}

View File

@ -0,0 +1,18 @@
package com.pudonghot.ambition.crm.form.update;
import com.pudonghot.ambition.crm.model.LocalProduct;
import lombok.Getter;
import lombok.Setter;
/**
* @version 0.0.1
* @since 0.0.1
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* May 10, 2016 1:42:01 PM
*/
@Getter
@Setter
public class LocalProductImageFormForUpdate extends LocalProductAttachmentFormForUpdate {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,37 @@
package com.pudonghot.ambition.crm.model;
import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.model.M3;
import me.chyxion.tigon.mybatis.Table;
import me.chyxion.tigon.mybatis.Transient;
import lombok.experimental.FieldNameConstants;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Nov 24, 2018 13:42:24
*/
@Getter
@Setter
@Table("crm_local_product")
@FieldNameConstants(prefix = "")
public class LocalProduct extends M3<String, String> {
private static final long serialVersionUID = 1L;
// Properties
private String name;
private String namePrefix;
private String content;
private String owner;
// Transient Props
@Transient
private String images;
@Transient
private String imageTitles;
@Transient
private String attachments;
@Transient
private String attachmentTitles;
}

View File

@ -22,6 +22,8 @@ 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 = "chyxion";
// Column Names
public static final String ACCOUNT = "account";