@Entity
@Table(name = "tbl_admin")
public class Admin extends DataEntity<Admin, Long> implements Lockedable, Disabledable, LogicDeleteable {

	private static final long serialVersionUID = -5778996032468144613L;
	/** 用户名 */
	@NotNull
	@Size(min = 2, max = 20)
	@Pattern(regexp = Patterns.REGEX_USERNAME, message = "账号必须为4-16位字符")
	@Column(nullable = false, updatable = false, unique = true, length = 100)
	private String username;

	/** 密码 */
	// @NotNull
	// @Pattern(regexp = Patterns.REGEX_PASSWORD)
	// @Size(min = 4, max = 20)
	@Column(nullable = false)
	private String password;

	/** E-mail */
	@Size(max = 200)
	@Pattern(regexp = Patterns.REGEX_MAIL, message = "请输入正确的邮箱")
	private String email;

	/** 手机号码 */
	@Size(max = 200)
	@Pattern(regexp = Patterns.REGEX_MOBILE, message = "请输入正确的手机号码")
	private String mobile;

	/** 姓名 */
	@NotNull
	@Size(max = 200)
	private String name;

	private String photo;

	/** 是否禁用 */
	@Column(nullable = false)
	private boolean disabled = false;

	/** 是否锁定 */
	@Column(nullable = false)
	private boolean locked = false;

	/** 是否删除 */
	@Column(nullable = false)
	private boolean deleted = false;

	/** 连续登录失败次数 */
	@Column(nullable = false)
	private int loginFailureCount = 0;

	/** 锁定日期 */
	private Date lockedDate;

	/** 最后登录日期 */
	private Date lastLoginDate;

	/** 最后登录IP */
	private String lastLoginIp;

	public static final String ROLE_ADMIN = "admin";
	public static final String ROLE_SALESMAN = "salesman";
	public static final String ROLE_SALESMAN_TEMP = "salestemp";
	public static final String ROLE_SHIPPER = "shipper";
	public static final String ROLE_UNDEFINE = "undefine";

	/** 业务类型 admin、salesman、shipper */
	private String businessType = ROLE_ADMIN;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getMobile() {
		return mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhoto() {
		return photo;
	}

	public void setPhoto(String photo) {
		this.photo = photo;
	}

	public boolean isDisabled() {
		return disabled;
	}

	public void setDisabled(boolean disabled) {
		this.disabled = disabled;
	}

	public boolean isLocked() {
		return locked;
	}

	public void setLocked(boolean locked) {
		this.locked = locked;
	}

	public boolean isDeleted() {
		return deleted;
	}

	public void setDeleted(boolean deleted) {
		this.deleted = deleted;
	}

	public int getLoginFailureCount() {
		return loginFailureCount;
	}

	public void setLoginFailureCount(int loginFailureCount) {
		this.loginFailureCount = loginFailureCount;
	}

	public Date getLockedDate() {
		return lockedDate;
	}

	public void setLockedDate(Date lockedDate) {
		this.lockedDate = lockedDate;
	}

	public Date getLastLoginDate() {
		return lastLoginDate;
	}

	public void setLastLoginDate(Date lastLoginDate) {
		this.lastLoginDate = lastLoginDate;
	}

	public String getLastLoginIp() {
		return lastLoginIp;
	}

	public void setLastLoginIp(String lastLoginIp) {
		this.lastLoginIp = lastLoginIp;
	}

	@Override
	public void markDeleted() {
		this.deleted = true;
	}

	@Override
	public void markDisabled() {
		this.disabled = true;
	}

	@Override
	public void markLocked() {
		this.locked = true;
	}

	public String getBusinessType() {
		return businessType;
	}

	public void setBusinessType(String businessType) {
		this.businessType = businessType;
	}

	public boolean isAdmin() {
		return Objects.equal(ROLE_ADMIN, getBusinessType());
	}

	public boolean isSalesman() {
		return Objects.equal(ROLE_SALESMAN, getBusinessType());
	}

	public boolean isTempSalesman() {
		return Objects.equal(ROLE_SALESMAN_TEMP, getBusinessType());
	}

	public boolean isShipper() {
		return Objects.equal(ROLE_SHIPPER, getBusinessType());
	}
} @Entity
@Table(name = "tbl_order")
public class Order extends DataEntity<Admin, Long> {

	private static final long serialVersionUID = -5896090021660813322L;

	/** 订单编号 */
	@NotNull
	@Size(max = 100)
	@Column(nullable = false, unique = true, length = 100)
	private String sn;

	/** 订单状态 pending、inhand、confirmed、followup、canceled、completed */
	@Column(nullable = false)
	private String orderStatus = "pending";

	/** 配送状态 unshipped、shipped、received、returned */
	@Column(nullable = false)
	private String shippingStatus = "unshipped";

	/** 商品编号 */
	@ManyToOne(fetch = FetchType.LAZY)
	private Product product;

	/** 商品 */
	@ManyToOne(fetch = FetchType.LAZY)
	private ProductSku productSku;

	@Size(max = 200)
	private String specification;

	@Size(max = 100)
	private String consignee;

	/** 地址 */
	@Size(max = 200)
	private String address;

	/** 电话 */
	@Size(max = 100)
	private String phone;

	/** 下单时间 */
	private Date orderedDate;

	/** 订单金额 */
	@Min(0)
	private BigDecimal amount;

	/** 备注 */
	@Size(max = 500)
	@Column(length = 1000)
	private String memo;

	/** 备注 */
	@Lob
	private String remark;

	/** 销售员 */
	@ManyToOne(fetch = FetchType.LAZY)
	private Admin operator;

	/** 发货员 */
	@ManyToOne(fetch = FetchType.LAZY)
	private Admin shipper;

	@Size(max = 100)
	private String shippingSn;

	public String getSn() {
		return sn;
	}

	public void setSn(String sn) {
		this.sn = sn;
	}

	public String getOrderStatus() {
		return orderStatus;
	}

	public void setOrderStatus(String orderStatus) {
		this.orderStatus = orderStatus;
	}

	public String getShippingStatus() {
		return shippingStatus;
	}

	public void setShippingStatus(String shippingStatus) {
		this.shippingStatus = shippingStatus;
	}

	public Product getProduct() {
		return product;
	}

	public void setProduct(Product product) {
		this.product = product;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public Date getOrderedDate() {
		return orderedDate;
	}

	public void setOrderedDate(Date orderedDate) {
		this.orderedDate = orderedDate;
	}

	public BigDecimal getAmount() {
		return amount;
	}

	public void setAmount(BigDecimal amount) {
		this.amount = amount;
	}

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	public Admin getOperator() {
		return operator;
	}

	public void setOperator(Admin operator) {
		this.operator = operator;
	}

	public Admin getShipper() {
		return shipper;
	}

	public void setShipper(Admin shipper) {
		this.shipper = shipper;
	}

	public String getSpecification() {
		return specification;
	}

	public void setSpecification(String specification) {
		this.specification = specification;
	}

	public String getConsignee() {
		return consignee;
	}

	public void setConsignee(String consignee) {
		this.consignee = consignee;
	}

	public ProductSku getProductSku() {
		return productSku;
	}

	public void setProductSku(ProductSku productSku) {
		this.productSku = productSku;
	}

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	public String getShippingSn() {
		return shippingSn;
	}

	public void setShippingSn(String shippingSn) {
		this.shippingSn = shippingSn;
	}
} @Entity
@Table(name = "tbl_product")
public class Product extends DataEntity<Admin, Long> implements Defaultable {

	private static final long serialVersionUID = -6934807834358886933L;

	/** 编号 */
	@NotNull
	@Pattern(regexp = "^[0-9a-zA-Z_-]+$")
	@Size(max = 100)
	@Column(nullable = false, unique = true, length = 100)
	private String sn;

	/** 名称 */
	@NotNull
	@Size(max = 200)
	@Column(nullable = false)
	private String name;

	/** 销售价 */
	@Min(0)
	@Digits(integer = 12, fraction = 3)
	@Column(nullable = false, precision = 21, scale = 6)
	private BigDecimal price;

	/** 市场价 */
	@Min(0)
	@Digits(integer = 12, fraction = 3)
	@Column(nullable = false, precision = 21, scale = 6)
	private BigDecimal marketPrice;

	/** 展示图片 */
	@Size(max = 200)
	private String image;

	/** 单位 */
	@Size(max = 100)
	private String unit;

	/** 重量 */
	@Min(0)
	private Integer weight;

	/** 库存 */
	@Min(0)
	private Integer stock;

	/** 是否上架 */
	private Boolean isMarketable;

	/** 是否列出 */
	@NotNull
	@Column(nullable = false)
	private boolean isList = false;

	/** 是否置顶 */
	@NotNull
	@Column(nullable = false)
	private boolean isTop;

	/** 简介 */
	@Size(max = 1000)
	private String summary;

	/** 介绍 */
	@Lob
	private String introduction;

	/** 备注 */
	@Size(max = 200)
	private String memo;

	/** 销量 */
	@Min(0)
	private Long sales;

	/** 商品图片 */
	@Valid
	@ElementCollection
	@CollectionTable(name = "tbl_product_image")
	private List<ProductImage> productImages = new ArrayList<>();

	@Convert(converter = SaleInfoListToStringConverter.class)
	private List<SaleInfo> saleInfos = new ArrayList<>();

	/** 规格 */
	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "tbl_product_specification")
	@OrderBy("sortNo asc")
	private Set<Specification> specifications = new HashSet<>();

	/** skus */
	@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true)
	private List<ProductSku> productSkus = new ArrayList<>();

	@Size(max = 200)
	private String express;

	@Size(max = 200)
	private String monthSales;

	/** 页面标题 */
	@Size(max = 200)
	private String seoTitle;

	/** 页面关键词 */
	@Size(max = 200)
	private String seoKeywords;

	/** 页面描述 */
	@Size(max = 200)
	private String seoDescription;

	/** 是否默认 */
	@Column(nullable = false)
	private boolean defaulted = false;

	public String getSn() {
		return sn;
	}

	public void setSn(String sn) {
		this.sn = sn;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public BigDecimal getPrice() {
		return price;
	}

	public void setPrice(BigDecimal price) {
		this.price = price;
	}

	public BigDecimal getMarketPrice() {
		return marketPrice;
	}

	public void setMarketPrice(BigDecimal marketPrice) {
		this.marketPrice = marketPrice;
	}

	public String getImage() {
		return image;
	}

	public void setImage(String image) {
		this.image = image;
	}

	public String getUnit() {
		return unit;
	}

	public void setUnit(String unit) {
		this.unit = unit;
	}

	public Integer getWeight() {
		return weight;
	}

	public void setWeight(Integer weight) {
		this.weight = weight;
	}

	public Integer getStock() {
		return stock;
	}

	public void setStock(Integer stock) {
		this.stock = stock;
	}

	public Boolean getIsMarketable() {
		return isMarketable;
	}

	public void setIsMarketable(Boolean isMarketable) {
		this.isMarketable = isMarketable;
	}

	public boolean isList() {
		return isList;
	}

	public void setList(boolean isList) {
		this.isList = isList;
	}

	public boolean isTop() {
		return isTop;
	}

	public void setTop(boolean isTop) {
		this.isTop = isTop;
	}

	public String getSummary() {
		return summary;
	}

	public void setSummary(String summary) {
		this.summary = summary;
	}

	public String getIntroduction() {
		return introduction;
	}

	public void setIntroduction(String introduction) {
		this.introduction = introduction;
	}

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	public Long getSales() {
		return sales;
	}

	public void setSales(Long sales) {
		this.sales = sales;
	}

	public List<ProductImage> getProductImages() {
		return productImages;
	}

	public void setProductImages(List<ProductImage> productImages) {
		this.productImages = productImages;
	}

	public List<SaleInfo> getSaleInfos() {
		return saleInfos;
	}

	public void setSaleInfos(List<SaleInfo> saleInfos) {
		this.saleInfos = saleInfos;
	}

	public String getSeoTitle() {
		return seoTitle;
	}

	public void setSeoTitle(String seoTitle) {
		this.seoTitle = seoTitle;
	}

	public String getSeoKeywords() {
		return seoKeywords;
	}

	public void setSeoKeywords(String seoKeywords) {
		this.seoKeywords = seoKeywords;
	}

	public String getSeoDescription() {
		return seoDescription;
	}

	public void setSeoDescription(String seoDescription) {
		this.seoDescription = seoDescription;
	}

	@Override
	public boolean isDefaulted() {
		return this.defaulted;
	}

	@Override
	public void setDefaulted(boolean defaulted) {
		this.defaulted = defaulted;
	}

	@Override
	public void markDefaulted() {
		this.defaulted = true;
	}

	public String getExpress() {
		return express;
	}

	public void setExpress(String express) {
		this.express = express;
	}

	public String getMonthSales() {
		return monthSales;
	}

	public void setMonthSales(String monthSales) {
		this.monthSales = monthSales;
	}

	public Set<Specification> getSpecifications() {
		return specifications;
	}

	public void setSpecifications(Set<Specification> specifications) {
		this.specifications = specifications;
	}

	public List<ProductSku> getProductSkus() {
		return productSkus;
	}

	public void setProductSkus(List<ProductSku> productSkus) {
		this.productSkus = productSkus;
	}

	public static class SaleInfo implements Sortable {
		private String content;

		private Integer sortNo;

		public String getContent() {
			return content;
		}

		public void setContent(String content) {
			this.content = content;
		}

		@Override
		public Integer getSortNo() {
			return sortNo;
		}

		@Override
		public void setSortNo(Integer sortNo) {
			this.sortNo = sortNo;
		}

	}

} @Entity
@Table(name = "tbl_product_sku")
public class ProductSku extends BaseEntity<Long> implements Sortable {

	private static final long serialVersionUID = -813614996133939277L;

	/** 销售价 */
	@Min(0)
	@Digits(integer = 12, fraction = 3)
	@Column(nullable = false, precision = 21, scale = 6)
	private BigDecimal price;

	/** 市场价 */
	@Min(0)
	@Digits(integer = 12, fraction = 3)
	@Column(nullable = false, precision = 21, scale = 6)
	private BigDecimal marketPrice;

	/** 排序 */
	private Integer sortNo;

	/** 商品编号 */
	@ManyToOne(fetch = FetchType.LAZY)
	private Product product;

	/** 规格值 */
	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "tbl_sku_specification_value")
	@OrderBy("specification asc")
	private Set<SpecificationValue> specificationValues = new HashSet<SpecificationValue>();

	@Override
	public Integer getSortNo() {
		return sortNo;
	}

	@Override
	public void setSortNo(Integer sortNo) {
		this.sortNo = sortNo;
	}

	public BigDecimal getPrice() {
		return price;
	}

	public void setPrice(BigDecimal price) {
		this.price = price;
	}

	public BigDecimal getMarketPrice() {
		return marketPrice;
	}

	public void setMarketPrice(BigDecimal marketPrice) {
		this.marketPrice = marketPrice;
	}

	public Set<SpecificationValue> getSpecificationValues() {
		return specificationValues;
	}

	public void setSpecificationValues(Set<SpecificationValue> specificationValues) {
		this.specificationValues = specificationValues;
	}

	public Product getProduct() {
		return product;
	}

	public void setProduct(Product product) {
		this.product = product;
	}

} @Entity
@Table(name = "tbl_specification_value")
public class SpecificationValue extends SortEntity<Admin, Long> {

	private static final long serialVersionUID = 4615230439932425376L;

	/** 名称 */
	@NotNull
	@Size(max = 200)
	@Column(nullable = false)
	private String name;

	/** 图片 */
	@Size(max = 200)
	private String image;

	/** 规格 */
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(nullable = false)
	private Specification specification;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getImage() {
		return image;
	}

	public void setImage(String image) {
		this.image = image;
	}

	public Specification getSpecification() {
		return specification;
	}

	public void setSpecification(Specification specification) {
		this.specification = specification;
	}

} public interface AdminRepository extends BaseJpaRepository<Admin, Long>, QuerydslBinderCustomizer<QAdmin> {

	Admin findByUsername(String username);

	@Override
	default void customize(QuerydslBindings bindings, QAdmin root) {
		bindings.bind(root.username, root.name, root.email, root.mobile).first((path, value) -> path.contains(value));
		bindings.excluding(root.password);
	}
} public interface IndexSliderRepository extends BaseJpaRepository<IndexSlider, Long> {

} public interface OrderRepository extends BaseJpaRepository<Order, Long>, QuerydslBinderCustomizer<QOrder> {

	@Override
	default void customize(QuerydslBindings bindings, QOrder root) {
		bindings.bind(root.sn, root.product.sn, root.product.name, root.consignee, root.phone, root.address)
				.first((path, value) -> path.contains(value));
	}

} public interface ProductRepository
		extends BaseJpaRepository<Product, Long>, QuerydslBinderCustomizer<QProduct>, ProductRepositoryCustom {

	@Override
	default void customize(QuerydslBindings bindings, QProduct root) {
		bindings.bind(root.name, root.sn).first((path, value) -> path.contains(value));
	}

} public interface ProductSkuRepository extends BaseJpaRepository<ProductSku, Long> {

} public class CurrentUserDetails extends CustomUserDetails<Long, Admin> {

	public CurrentUserDetails(Long id, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
			Collection<? extends GrantedAuthority> authorities) {
		super(id, username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
	}

	private static final long serialVersionUID = 8220061317304759492L;

	@Override
	public Admin getCustomUser() {
		return adminRepository.getOne(getId());
	}
}
 public class SettingUtils {

	private static Setting settingHolder;
	private static Object holder = new Object();

	public static Setting get() {
		if (settingHolder == null) {
			synchronized (holder) {
				if (settingHolder == null) {
					Setting setting = Setting.getInstance();
					String settingXmlPath = StaticConfigSupplier.getConfiguration().getString("setting.xml_path",
							"/setting.xml");
					try {
						File settingFile = new ClassPathResource(settingXmlPath).getFile();
						Document document = new SAXReader().read(settingFile);
						@SuppressWarnings("unchecked")
						List<Element> elements = document.selectNodes("/config/setting");
						BeanWrapperImpl beanWrapperImpl = new BeanWrapperImpl(setting);
						ConversionService conversionService = WebSpringContext.getBean(ConversionService.class);
						for (Element element : elements) {
							String name = element.attributeValue("name");
							String value = element.attributeValue("value");
							beanWrapperImpl.setPropertyValue(name,
									conversionService.convert(value, beanWrapperImpl.getPropertyType(name)));
						}
					} catch (IOException | DocumentException e) {
						e.printStackTrace();
					}
					settingHolder = setting;
				}
			}

		}

		return settingHolder;
	}

	public static void set(Setting setting) {
		String settingXmlPath = StaticConfigSupplier.getConfiguration().getString("setting.xml_path", "/setting.xml");
		try {
			File settingFile = new ClassPathResource(settingXmlPath).getFile();
			Document document = new SAXReader().read(settingFile);
			@SuppressWarnings("unchecked")
			List<Element> elements = document.selectNodes("/config/setting");
			BeanWrapperImpl beanWrapperImpl = new BeanWrapperImpl(setting);
			ConversionService conversionService = WebSpringContext.getBean(ConversionService.class);
			for (Element element : elements) {
				String name = element.attributeValue("name");
				String value = conversionService.convert(beanWrapperImpl.getPropertyValue(name), String.class);
				Attribute attribute = element.attribute("value");
				attribute.setValue(value);
			}
			
			FileOutputStream fileOutputStream = null;
			XMLWriter xmlWriter = null;
			try {
				OutputFormat outputFormat = OutputFormat.createPrettyPrint();
				outputFormat.setEncoding("UTF-8");
				outputFormat.setIndent(true);
				outputFormat.setIndent("	");
				outputFormat.setNewlines(true);
				fileOutputStream = new FileOutputStream(settingFile);
				xmlWriter = new XMLWriter(fileOutputStream, outputFormat);
				xmlWriter.write(document);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (xmlWriter != null) {
					try {
						xmlWriter.close();
					} catch (IOException e) {
					}
				}
				IOUtils.closeQuietly(fileOutputStream);
			}
		} catch (IOException | DocumentException e) {
			e.printStackTrace();
		}
	}

}
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.expression.Numbers;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.whenling.castle.core.helper.Patterns;
import com.whenling.castle.repo.domain.Result;
import com.whenling.castle.security.CurrentUser;
import com.whenling.shop.entity.Admin;
import com.whenling.shop.entity.Order;
import com.whenling.shop.entity.Product;
import com.whenling.shop.entity.ProductSku;
import com.whenling.shop.entity.QAdmin;
import com.whenling.shop.entity.QIndexSlider;
import com.whenling.shop.entity.QOrder;
import com.whenling.shop.entity.QProduct;
import com.whenling.shop.entity.SpecificationValue;
import com.whenling.shop.repo.AdminRepository;
import com.whenling.shop.repo.IndexSliderRepository;
import com.whenling.shop.repo.OrderRepository;
import com.whenling.shop.repo.ProductRepository;
import com.whenling.shop.repo.ProductSkuRepository;
import com.whenling.shop.security.AdminDetailsService.CurrentUserDetails;
import com.whenling.shop.support.setting.SettingUtils;
package com.whenling.shop.controller;




@Controller
@RequestMapping
public class IndexController {

	@Autowired
	private AdminRepository adminRepository;

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private ProductRepository productRepository;

	@Autowired
	private ProductSkuRepository productSkuRepository;

	@Autowired
	private OrderRepository orderRepository;