Commit 4e08442a authored by hanfeng's avatar hanfeng

Merge branch 'master-invoice-modular' into dev

parents 56e57d3e 4a22dcb5
...@@ -23,8 +23,8 @@ import java.util.List; ...@@ -23,8 +23,8 @@ import java.util.List;
* @author Administrator * @author Administrator
*/ */
@Service @Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class AppStaffUserBiz extends BaseBiz<AppStaffUserMapper, AppStaffUser> { public class AppStaffUserBiz extends BaseBiz<AppStaffUserMapper, AppStaffUser> {
private final AppUserDetailBiz appUserDetailBiz; private final AppUserDetailBiz appUserDetailBiz;
private final Integer POSITION_ID=4; private final Integer POSITION_ID=4;
......
...@@ -3,13 +3,11 @@ package com.xxfc.platform.order.feign; ...@@ -3,13 +3,11 @@ package com.xxfc.platform.order.feign;
import com.github.wxiaoqi.security.auth.client.annotation.IgnoreClientToken; import com.github.wxiaoqi.security.auth.client.annotation.IgnoreClientToken;
import com.github.wxiaoqi.security.auth.client.annotation.IgnoreUserToken; import com.github.wxiaoqi.security.auth.client.annotation.IgnoreUserToken;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse; import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.xxfc.platform.order.entity.OrderInvoice;
import com.xxfc.platform.order.pojo.dto.OrderDTO; import com.xxfc.platform.order.pojo.dto.OrderDTO;
import com.xxfc.platform.order.pojo.order.OrderPageVO; import com.xxfc.platform.order.pojo.order.OrderPageVO;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List; import java.util.List;
...@@ -27,4 +25,8 @@ public interface OrderFeign { ...@@ -27,4 +25,8 @@ public interface OrderFeign {
@GetMapping(value = "/count/basebase/findByOrderIds") @GetMapping(value = "/count/basebase/findByOrderIds")
public ObjectRestResponse<List<OrderDTO>> findOrdersByorderId(@RequestParam(value = "orderIds") List<Integer> orderIds); public ObjectRestResponse<List<OrderDTO>> findOrdersByorderId(@RequestParam(value = "orderIds") List<Integer> orderIds);
@PostMapping(value = "/order/invoice/updateByOrderId")
ObjectRestResponse updateByOrderId(@RequestBody OrderInvoice orderInvoice);
} }
...@@ -19,6 +19,8 @@ import org.apache.commons.lang3.StringUtils; ...@@ -19,6 +19,8 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.HashSet; import java.util.HashSet;
...@@ -164,4 +166,17 @@ public class OrderInvoiceBiz extends BaseBiz<OrderInvoiceMapper, OrderInvoice> { ...@@ -164,4 +166,17 @@ public class OrderInvoiceBiz extends BaseBiz<OrderInvoiceMapper, OrderInvoice> {
} }
@Transactional(rollbackFor = Exception.class)
public boolean updateByOrderId(OrderInvoice orderInvoice) {
try {
Example example = new Example(OrderInvoice.class);
example.createCriteria().andEqualTo("orderIds",orderInvoice.getOrderIds());
mapper.updateByExample(orderInvoice,example);
return true;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("修改状态异常={}",e);
return false;
}
}
} }
...@@ -47,4 +47,9 @@ public class OrderInvoiceController extends BaseController<OrderInvoiceBiz, Orde ...@@ -47,4 +47,9 @@ public class OrderInvoiceController extends BaseController<OrderInvoiceBiz, Orde
return baseBiz.update(orderInvoice); return baseBiz.update(orderInvoice);
} }
@PostMapping(value = "/updateByOrderId")
public ObjectRestResponse updateByOrderId(@RequestBody OrderInvoice orderInvoice) {
boolean flag = baseBiz.updateByOrderId(orderInvoice);
return ObjectRestResponse.succ();
}
} }
package com.xxfc.platform.universal.constant;
import lombok.Data;
/**
* @Auther: Administrator
* @Date: 2020/1/6 18:04
* @Description:
*/
public enum InvoiceStatusEum {
UNBILLED(0,"未开票"),ONGOING(1,"开票中"),SUCCESS(2,"开票成功"),
FAILURE(3,"开票失败");
private Integer code;
private String msg;
InvoiceStatusEum(int code, String msg) {
this.code=code;
this.msg=msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
package com.xxfc.platform.universal.entity;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
@Data
@Table(name = "invoice")
@ApiModel
public class Invoice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 用户id
*/
private Integer userId;
/**
* 订单id :多个以逗号隔开
*/
private String orderId;
/**
* 公司代码
*/
private String companyCode;
/**
* 发票订单号
*/
private String orderNo;
/**
* 机构代码
*/
private String orgCode;
/**
* 发票代码
*/
private String invoiceCode;
/**
* 发票号码
*/
private String invoiceNo;
/**
* 开票日期
*/
private Date invoiceDate;
/**
* 发票分类:01 专票,02 货物运输业增值税专用发票,03 机动车销售统一发票,
* 04 增值税普通发票,10 增值税普通发票(电子),11 增值税普通发票(卷票),14 增值税普通发票(卷票)
*/
private Integer invoiceCategory;
/**
* 合计金额
*/
private BigDecimal invoiceAmount;
/**
* 合计税额
*/
private BigDecimal taxAmount;
/**
* 价税合计总额
*/
private BigDecimal invoiceSummaryAmount;
/**
* 价税合计总额大写
*/
private String invoiceSummaryAmountText;
/**
* 备注
*/
private String remark;
/**
* 验证码
*/
private String verifyCode;
/**
* 机器编号
*/
private String machineNo;
/**
* 密码区
*/
private String invoicePassword;
/**
* 地区
*/
private String areaCode;
/**
* 地区名称
*/
private String areaName;
/**
* 销售方名称
*/
private String salerName;
/**
* 销售方纳税人识别号
*/
private String salerTaxCode;
/**
* 销售方地址
*/
private String salerAddress;
/**
* 销售方电话
*/
private String salerPhone;
/**
* 销售方银行及账户
*/
private String salerAccountBank;
/**
* 购买方名称
*/
private String purchaserName;
/**
* 购买方纳税人识别号
*/
private String purchaserTaxCode;
/**
* 购方手机
*/
private String purchaserPhone;
/**
* 购方邮箱
*/
private String purchaserEmail;
/**
* 购买方地址电话
*/
private String purchaserAddressPhone;
/**
* 购买方银行账号
*/
private String purchaserAccountBank;
/**
* 收款人
*/
private String payee;
/**
* 复核人
*/
private String reviewer;
/**
* 开票人
*/
private String issuer;
// /**
// * 发票状态
// */
// private Integer invoiceStatus;
/**
* 附件
*/
private String attachment;
/**
* 原票代码
*/
private String originalCode;
/**
* 原票号
*/
private String originalNo;
/**
* 查验状态
*/
private Integer recognizeFlag;
/**
* 查验
*/
private Integer validState;
/**
* 开票状态 0:未开票;1:开票中;2:为开票成功;3:开票失败;
*/
private Integer status;
/**
* 发票图片链接
*/
private String pictureUrl;
/**
* 开票类型:1-正票;2-红票
*/
private Integer invoiceType=1;
/**
* 开票内容
*/
private String content;
/**
* 推送方式:-1,不推送;0,邮箱;1,手机(默认);2,邮箱、手机
*/
private Integer pushMode=2;
/**
* 开票代码
*/
private String invoiceSerialNum;
private String createBy;
private String updateBy;
private Date createTime;
private Date updateTime;
}
package com.xxfc.platform.universal.entity;
import lombok.Data;
import javax.persistence.Table;
import java.util.Date;
/**
* 发票最终查询结果,错误记录表
* @author Administrator
*/
@Data
@Table(name = "invoice_query_error")
public class InvoiceQueryError {
private Integer id;
private Integer invoiceId;
private String invoiceSerialNum;
private Integer status;
private Date creTime;
}
package com.xxfc.platform.universal.entity;
import lombok.Data;
import java.math.BigDecimal;
/**
* @Auther: Administrator
* @Date: 2020/1/3 11:20
* @Description:
*/
@Data
public class OrderData {
/**
* 产品名称
*/
private String orderName;
/**
* 产品数量
*/
private Integer num;
/**
* 是否含税价格 0:不含税,1:含税
*/
private Integer withTaxFlag=1;
/**
* 产品单价
*/
private BigDecimal price;
/**
* 税率
*/
private BigDecimal taxRate=new BigDecimal(0.03);
/**
* 单位
*/
private String unit="次";
}
package com.xxfc.platform.universal.entity.vo;
import com.xxfc.platform.universal.entity.OrderData;
import lombok.Data;
import java.util.List;
/**
* @Auther: Administrator
* @Date: 2020/1/3 14:43
* @Description:
*/
@Data
public class InvoiceVo {
/**
* 发票分类:01 专票,02 货物运输业增值税专用发票,03 机动车销售统一发票,
* 04 增值税普通发票,10 增值税普通发票(电子),11 增值税普通发票(卷票),14 增值税普通发票(卷票)
*/
private Integer invoiceCategory=10;
/**
* 订单id :多个以逗号隔开
*/
private String orderId;
/**
* 备注
*/
private String remark;
/**
* 购买方名称
*/
private String purchaserName;
/**
* 购买方纳税人识别号
*/
private String purchaserTaxCode;
/**
* 购方邮箱
*/
private String purchaserEmail;
/**
* 购买方地址电话
*/
private String purchaserAddressPhone;
/**
* 购买方银行账号
*/
private String purchaserAccountBank;
/**
* 开票内容
*/
private String content;
/**
* 购方手机
*/
private String purchaserPhone;
private List<OrderData> orders;
}
package com.xxfc.platform.universal.utils;
import java.math.BigDecimal;
/**
*
*
* 数字转换为汉语中人民币的大写<br>
*
*/
public class NumberToCN {
/**
* 汉语中数字大写
*/
private static final String[] CN_UPPER_NUMBER = { "零", "壹", "贰", "叁", "肆",
"伍", "陆", "柒", "捌", "玖" };
/**
* 汉语中货币单位大写,这样的设计类似于占位符
*/
private static final String[] CN_UPPER_MONETRAY_UNIT = { "分", "角", "元",
"拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾",
"佰", "仟" };
/**
* 特殊字符:整
*/
private static final String CN_FULL = "整";
/**
* 特殊字符:负
*/
private static final String CN_NEGATIVE = "负";
/**
* 金额的精度,默认值为2
*/
private static final int MONEY_PRECISION = 2;
/**
* 特殊字符:零元整
*/
private static final String CN_ZEOR_FULL = "零元" + CN_FULL;
/**
* 把输入的金额转换为汉语中人民币的大写
*
* @param numberOfMoney
* 输入的金额
* @return 对应的汉语大写
*/
public static String number2CNMontrayUnit(BigDecimal numberOfMoney) {
StringBuffer sb = new StringBuffer();
// -1, 0, or 1 as the value of this BigDecimal is negative, zero, or
// positive.
int signum = numberOfMoney.signum();
// 零元整的情况
if (signum == 0) {
return CN_ZEOR_FULL;
}
// 这里会进行金额的四舍五入
long number = numberOfMoney.movePointRight(MONEY_PRECISION)
.setScale(0, 4).abs().longValue();
// 得到小数点后两位值
long scale = number % 100;
int numUnit = 0;
int numIndex = 0;
boolean getZero = false;
// 判断最后两位数,一共有四中情况:00 = 0, 01 = 1, 10, 11
if (!(scale > 0)) {
numIndex = 2;
number = number / 100;
getZero = true;
}
if ((scale > 0) && (!(scale % 10 > 0))) {
numIndex = 1;
number = number / 10;
getZero = true;
}
int zeroSize = 0;
while (true) {
if (number <= 0) {
break;
}
// 每次获取到最后一个数
numUnit = (int) (number % 10);
if (numUnit > 0) {
if ((numIndex == 9) && (zeroSize >= 3)) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
}
if ((numIndex == 13) && (zeroSize >= 3)) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
}
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
sb.insert(0, CN_UPPER_NUMBER[numUnit]);
getZero = false;
zeroSize = 0;
} else {
++zeroSize;
if (!(getZero)) {
sb.insert(0, CN_UPPER_NUMBER[numUnit]);
}
if (numIndex == 2) {
if (number > 0) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
}
} else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
}
getZero = true;
}
// 让number每次都去掉最后一个数
number = number / 10;
++numIndex;
}
// 如果signum == -1,则说明输入的数字为负数,就在最前面追加特殊字符:负
if (signum == -1) {
sb.insert(0, CN_NEGATIVE);
}
// 输入的数字小数点后两位为"00"的情况,则要在最后追加特殊字符:整
if (!(scale > 0)) {
sb.append(CN_FULL);
}
return sb.toString();
}
public static void main(String[] args) {
double money = 2500.32;
BigDecimal numberOfMoney = new BigDecimal(money);
String s = NumberToCN.number2CNMontrayUnit(numberOfMoney);
System.out.println("你输入的金额为:【" + money + "】 #--# [" + s.toString()
+ "]");
}
}
\ No newline at end of file
package com.xxfc.platform.universal.utils;
public class SnowflakeIdWorker {
// ==============================Fields==================
/** 开始时间截 (2019-01-07) */
private final long twepoch = 1578326400000L;
/** 机器id所占的位数 */
private final long workerIdBits = 5L;
/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;
/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 支持的最大数据标识id,结果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/** 序列在id中占的位数 */
private final long sequenceBits = 12L;
/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 数据标识id向左移17位(12+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;
/** 时间截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 工作机器ID(0~31) */
private long workerId;
/** 数据中心ID(0~31) */
private long datacenterId;
/** 毫秒内序列(0~4095) */
private long sequence = 0L;
/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;
//==============================Constructors====================
/**
* 构造函数
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
// ==============================Methods=================================
/**
* 获得下一个ID (该方法是线程安全的)
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
* @return 当前时间(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
//==============================Test=============================================
/** 测试 */
public static void main(String[] args) {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 1000; i++) {
long id = idWorker.nextId();
System.out.println(Long.toBinaryString(id));
System.out.println(id);
}
}
}
\ No newline at end of file
...@@ -105,6 +105,18 @@ ...@@ -105,6 +105,18 @@
<version>2.0-SNAPSHOT</version> <version>2.0-SNAPSHOT</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- 诺诺发票sdk-->
<dependency>
<groupId>com.nuonuo</groupId>
<artifactId>open-sdk</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.xxfc.platform</groupId>
<artifactId>xx-order-api</artifactId>
<version>2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
package com.xxfc.platform.universal.biz;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.xxfc.platform.universal.entity.InvoiceQueryError;
import com.xxfc.platform.universal.mapper.InvoiceQueryErrorMapper;
import org.springframework.stereotype.Service;
/**
* @Auther: Administrator
* @Date: 2020/1/8 10:48
* @Description:
*/
@Service
public class InvoiceQueryErrorBiz extends BaseBiz<InvoiceQueryErrorMapper, InvoiceQueryError> {
}
package com.xxfc.platform.universal.controller;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.rest.BaseController;
import com.xxfc.platform.universal.entity.Invoice;
import com.xxfc.platform.universal.entity.vo.InvoiceVo;
import com.xxfc.platform.universal.service.InvoiceBiz;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 发票接口
* @Auther: Administrator
* @Date: 2019/12/31 09:57
* @Description:
*/
@RestController
@RequestMapping("/invoice")
public class InvoiceController extends BaseController<InvoiceBiz, Invoice> {
/**
* 根据开票代码获取税号
* @param code
* @return
*/
@GetMapping("/dutyParagraph")
public ObjectRestResponse getDutyParagraphByCode(@RequestParam("code")String code){
String dutyParagraph = baseBiz.getDutyParagraphByCode(code);
return ObjectRestResponse.succ(dutyParagraph);
}
/**
* 模糊查询获取公司全名和开票代码
* @param name
* @return
*/
@GetMapping("/corporateName")
public ObjectRestResponse corporateName(@RequestParam("name")String name){
List<Map<String, String>> mapList = baseBiz.corporateName(name);
return ObjectRestResponse.succ(mapList);
}
@PostMapping("/invoicing")
public ObjectRestResponse invoicing(@RequestBody InvoiceVo invoiceVo) throws Exception {
String invoicing = baseBiz.Invoicing(invoiceVo);
return ObjectRestResponse.succ(invoicing);
}
@GetMapping("/result")
public ObjectRestResponse invoiceResult(@RequestParam("invoiceSerialNum")String invoiceSerialNum){
List<Map<String,Object>> result= baseBiz.invoiceResult(invoiceSerialNum);
return ObjectRestResponse.succ(result);
}
}
package com.xxfc.platform.universal.mapper;
import com.xxfc.platform.universal.entity.Invoice;
import tk.mybatis.mapper.common.Mapper;
/**
* @author Administrator
* @Auther: Administrator
* @Date: 2020/1/6 16:38
* @Description:
*/
public interface InvoiceMapper extends Mapper<Invoice> {
}
package com.xxfc.platform.universal.mapper;
import com.xxfc.platform.universal.entity.InvoiceQueryError;
import tk.mybatis.mapper.common.Mapper;
/**
* @author Administrator
* @Date: 2020/1/8 10:49
* @Description:
*/
public interface InvoiceQueryErrorMapper extends Mapper<InvoiceQueryError> {
}
package com.xxfc.platform.universal.service;
import com.github.wxiaoqi.security.auth.client.config.UserAuthConfig;
import com.github.wxiaoqi.security.auth.client.jwt.UserAuthUtil;
import com.github.wxiaoqi.security.auth.common.util.jwt.IJWTInfo;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.xxfc.platform.order.entity.OrderInvoice;
import com.xxfc.platform.order.feign.OrderFeign;
import com.xxfc.platform.universal.biz.InvoiceQueryErrorBiz;
import com.xxfc.platform.universal.constant.InvoiceStatusEum;
import com.xxfc.platform.universal.entity.Invoice;
import com.xxfc.platform.universal.entity.InvoiceQueryError;
import com.xxfc.platform.universal.entity.OrderData;
import com.xxfc.platform.universal.entity.vo.InvoiceVo;
import com.xxfc.platform.universal.mapper.InvoiceMapper;
import com.xxfc.platform.universal.service.invoice.InvoiceDao;
import com.xxfc.platform.universal.utils.NumberToCN;
import com.xxfc.platform.universal.utils.SnowflakeIdWorker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 发票服务
*
* @author Administrator
* @Date: 2019/12/31 10:05
* @Description:
*/
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @_({@Autowired}))
public class InvoiceBiz extends BaseBiz<InvoiceMapper, Invoice> {
private final InvoiceDao invoiceDao;
private final UserAuthConfig userAuthConfig;
private final HttpServletRequest request;
private final UserAuthUtil userAuthUtil;
private final InvoiceQueryErrorBiz invoiceQueryErrorBiz;
private final OrderFeign orderFeign;
private static final SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(0, 0);
@Value("${invoice.salerName}")
private String salerName;
@Value("${invoice.salerTel}")
private String salerTel;
@Value("${invoice.salerTaxNum}")
private String salerTaxNum;
@Value("${invoice.salerAddress}")
private String salerAddress;
@Value("${invoice.salerAccount}")
private String salerAccount;
/**
* 收款人
*/
@Value("${invoice.payee}")
private String payee;
/**
* 复核人
*/
@Value("${invoice.checker}")
private String checker;
/**
* 开票人
*/
@Value("${invoice.clerk}")
private String clerk;
/**
* 推送方式:-1,不推送;0,邮箱;1,手机(默认);2,邮箱、手机
*/
@Value("${invoice.pushMode}")
private Integer pushMode = 2;
private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(8);
/**
* 通过公司代码获取公司税号
*
* @param code
* @return
*/
public String getDutyParagraphByCode(String code) {
return invoiceDao.getDutyParagraphByCode(code);
}
/**
* 通过公司名获取公司全名和公司代码
*
* @param name
* @return
*/
public List<Map<String, String>> corporateName(String name) {
return invoiceDao.corporateName(name);
}
/**
* 开票
*
* @param invoiceVo
* @return
* @throws Exception
*/
public String Invoicing(InvoiceVo invoiceVo) throws Exception {
Invoice invoice = new Invoice();
long on = snowflakeIdWorker.nextId();
BeanUtils.copyProperties(invoiceVo, invoice);
invoice.setOrderNo(String.valueOf(on));
invoice.setSalerName(salerName);
invoice.setSalerTaxCode(salerTaxNum);
invoice.setSalerAddress(salerAddress);
invoice.setSalerPhone(salerTel);
invoice.setSalerAccountBank(salerAccount);
invoice.setPayee(payee);
invoice.setReviewer(checker);
invoice.setIssuer(clerk);
invoice.setPushMode(pushMode);
Date invoiceDate = new Date();
invoice.setCreateTime(invoiceDate);
invoice.setInvoiceDate(invoiceDate);
List<OrderData> orders = invoiceVo.getOrders();
//调用第三方开票
String invoiceSerialNum = invoiceDao.Invoicing(invoice, orders);
insertInvoic(invoice, invoiceSerialNum, orders);
return invoiceSerialNum;
}
/**
* 保存发票信息
*
* @param invoice
* @param invoiceSerialNum
* @param orders
* @throws Exception
*/
public void insertInvoic(Invoice invoice, String invoiceSerialNum, List<OrderData> orders) throws Exception {
IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(userAuthConfig.getToken(request));
invoice.setUserId(Integer.valueOf(infoFromToken.getId()));
invoice.setCreateBy(infoFromToken.getName());
invoice.setInvoiceSerialNum(invoiceSerialNum);
BigDecimal taxExcludedAmount = BigDecimal.ZERO;
BigDecimal taxIncludedAmount = BigDecimal.ZERO;
for (OrderData order : orders) {
if (order.getWithTaxFlag()==1) {
//含价税合计
BigDecimal taxIncluded = order.getPrice();
//税率
BigDecimal taxRate = order.getTaxRate();
//不含税单价
BigDecimal TaxFree = taxIncluded.divide(taxRate.add(new BigDecimal(1)),2,BigDecimal.ROUND_HALF_UP);
taxExcludedAmount=taxExcludedAmount.add(TaxFree);
taxIncludedAmount=taxIncludedAmount.add(taxIncluded);
}else {
//不含税单价
BigDecimal taxFree = order.getPrice();
//税率
BigDecimal taxRate = order.getTaxRate();
//含税单价
BigDecimal taxIncluded = taxFree.multiply(taxRate.add(new BigDecimal(1)));
taxExcludedAmount=taxExcludedAmount.add(taxFree);
taxIncludedAmount=taxIncludedAmount.add(taxIncluded);
}
}
invoice.setInvoiceAmount(taxExcludedAmount);
invoice.setTaxAmount(taxIncludedAmount.subtract(taxExcludedAmount));
invoice.setInvoiceSummaryAmount(taxIncludedAmount);
invoice.setInvoiceSummaryAmountText(NumberToCN.number2CNMontrayUnit(taxIncludedAmount));
//保存到数据库
mapper.insert(invoice);
//异步查询开票结果
schedule(invoiceSerialNum, invoice.getId(),invoice.getOrderId(),null, 0, 5,TimeUnit.SECONDS);
}
/**
* 或去查询结果
* @param invoiceSerialNum 发票流水号
* @param id 发票id
* @param orderId 订单id
* @param lastStatus 上一次产需到的开票状态
* @param count 已经查询过的次数
*/
public void queryInvoiceResult(String invoiceSerialNum, Integer id,String orderId,Integer lastStatus,Integer count) {
if (count==2){
InvoiceQueryError invoiceQueryError = new InvoiceQueryError();
invoiceQueryError.setInvoiceId(id);
invoiceQueryError.setInvoiceSerialNum(invoiceSerialNum);
invoiceQueryError.setCreTime(new Date());
invoiceQueryError.setStatus(lastStatus);
invoiceQueryErrorBiz.insertSelectiveRe(invoiceQueryError);
return;
}
List<Map<String, Object>> result = invoiceResult(invoiceSerialNum);
if (result==null){
schedule(invoiceSerialNum, id,orderId,null, ++count, 2,TimeUnit.HOURS);
return;
}
log.info("查询到的开票结果={}",result);
Map<String, Object> data = result.get(0);
Integer status = (Integer) data.get("status");
String invoiceFileUrl = (String) data.get("invoiceFileUrl");
String invoiceCode = (String) data.get("invoiceCode");
String invoiceNum = (String) data.get("invoiceNum");
String taxIncludedAmountStr = String.valueOf(data.get("taxIncludedAmount")) ;
String taxExcludedAmountStr = String.valueOf(data.get("taxExcludedAmount")) ;
Invoice invoice = new Invoice();
invoice.setId(id);
invoice.setStatus(status);
invoice.setUpdateTime(new Date());
if (StringUtils.isNotBlank(taxExcludedAmountStr)&&StringUtils.isNotBlank(taxIncludedAmountStr)) {
BigDecimal taxExcludedAmount = new BigDecimal(taxExcludedAmountStr);
BigDecimal taxIncludedAmount = new BigDecimal(taxIncludedAmountStr);
invoice.setInvoiceAmount(taxExcludedAmount);
invoice.setTaxAmount(taxIncludedAmount.subtract(taxExcludedAmount));
invoice.setInvoiceSummaryAmount(taxIncludedAmount);
invoice.setInvoiceSummaryAmountText(NumberToCN.number2CNMontrayUnit(taxIncludedAmount));
}
if (InvoiceStatusEum.ONGOING.getCode().equals(status) || InvoiceStatusEum.UNBILLED.getCode().equals(status)) {
schedule(invoiceSerialNum, id,orderId,status, ++count, 2,TimeUnit.HOURS);
}else if (InvoiceStatusEum.SUCCESS.getCode().equals(status)){
invoice.setInvoiceCode(invoiceCode);
invoice.setInvoiceNo(invoiceNum);
invoice.setPictureUrl(invoiceFileUrl);
setOrderInvoice(orderId, status, invoiceFileUrl);
}else {
setOrderInvoice(orderId, status,null);
}
mapper.updateByPrimaryKeySelective(invoice);
}
/**
* 设置订单发票表
* @param orderId 订单id
* @param status 发票查询状态
* @param invoiceFileUrl 发票pdf地址
*/
public void setOrderInvoice(String orderId, Integer status, String invoiceFileUrl) {
//设置发票订单表
OrderInvoice orderInvoice = new OrderInvoice();
orderInvoice.setOrderIds(orderId);
orderInvoice.setInvoiceUrl(invoiceFileUrl);
orderInvoice.setStatus(status);
orderFeign.updateByOrderId(orderInvoice);
}
/**
* 再次查询开票结果
* @param invoiceSerialNum
* @param id
* @param orderId
* @param lastStatus
* @param count
* @param time
* @param timeUnit
*/
public void schedule(String invoiceSerialNum, Integer id,String orderId,Integer lastStatus, int count, int time,TimeUnit timeUnit) {
executorService.schedule(() -> {
queryInvoiceResult(invoiceSerialNum, id,orderId,lastStatus, count);
}, time, timeUnit);
}
/**
* 获取开票结果
*
* @param invoiceSerialNum
* @return
*/
public List<Map<String, Object>> invoiceResult(String invoiceSerialNum) {
return invoiceDao.invoiceResult(invoiceSerialNum);
}
}
package com.xxfc.platform.universal.service.invoice;
import com.xxfc.platform.universal.entity.Invoice;
import com.xxfc.platform.universal.entity.OrderData;
import java.util.List;
import java.util.Map;
/**
* @Auther: Administrator
* @Date: 2019/12/31 10:23
* @Description:
*/
public interface InvoiceDao {
List<Map<String, String>> corporateName(String name);
String getDutyParagraphByCode(String code);
String Invoicing(Invoice invoice, List<OrderData> orders);
List<Map<String,Object>> invoiceResult(String invoiceSerialNum);
}
package com.xxfc.platform.universal.service.invoice.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.google.common.collect.Lists;
import com.xxfc.platform.universal.entity.Invoice;
import com.xxfc.platform.universal.entity.OrderData;
import com.xxfc.platform.universal.service.invoice.InvoiceDao;
import lombok.extern.slf4j.Slf4j;
import nuonuo.open.sdk.NNOpenSDK;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @Auther: Administrator
* @Date: 2019/12/31 10:26
* @Description:
*/
@Service
@Primary
@Slf4j
public class InvoiceDaoImpl implements InvoiceDao {
@Autowired
private RedisTemplate redisTemplate;
/**
* 诺诺-极速开票
*/
@Value("${invoice.nuonuo.js.taxnum}")
private String taxnum;
@Value("${invoice.nuonuo.js.appKey}")
private String appKey;
@Value("${invoice.nuonuo.js.appSecret}")
private String appSecret;
/**
* 诺诺-诺诺发票
*/
@Value("${invoice.nuonuo.nn.taxnum}")
private String taxnum2;
@Value("${invoice.nuonuo.nn.appKey}")
private String appKey2;
@Value("${invoice.nuonuo.nn.appSecret}")
private String appSecret2;
@Value("${invoice.nuonuo.queryUrl}")
private String queryUrl ;
@Value("${invoice.nuonuo.invoiceUrl}")
private String invoiceUrl ;
/**
* 给定的部分名,获取税号
*
* @param name
* @return
*/
@Override
public List<Map<String, String>> corporateName(String name) {
NNOpenSDK sdk = NNOpenSDK.getIntance();
HashMap<String, String> map = new HashMap();
map.put("q", name);
String content = JSON.toJSONString(map);
//方法名
String method = "nuonuo.speedBilling.prefixQuery";
// SDK请求地址
String url = "https://sdk.nuonuo.com/open/v1/services";
// 唯一标识,由企业自己生成32位随机码
String senid = UUID.randomUUID().toString().replace("-", "");
String result = sdk.sendPostSyncRequest(url, senid, appKey, appSecret, getTokenJS(), taxnum, method, content);
JSONObject jsonObject = JSON.parseObject(result);
List<Map<String, String>> mapList = (List<Map<String, String>>) jsonObject.get("result");
if (CollectionUtil.isNotEmpty(mapList)) {
return mapList;
}
return new ArrayList<>();
}
/**
* 根据开票代码,调用第三方接口获取税号
*
* @param code
* @return
*/
@Override
public String getDutyParagraphByCode(String code) {
NNOpenSDK sdk = NNOpenSDK.getIntance();
// API方法名
String method = "nuonuo.speedBilling.queryNameAndTaxByCode";
// SDK请求地址
String url = "https://sdk.jss.com.cn/open/v1/services";
HashMap<String, String> map = new HashMap();
map.put("code", code);
String content = JSON.toJSONString(map);
// 唯一标识,由企业自己生成32位随机码
String senid = UUID.randomUUID().toString().replace("-", "");
String result = sdk.sendPostSyncRequest(url, senid, appKey, appSecret, getTokenJS(), taxnum, method, content);
JSONObject jsonObject = JSON.parseObject(result);
Map<String, String> resultMap = (Map<String, String>) jsonObject.get("result");
return resultMap.get("kpCode");
}
/**
* 调用第三方开票接口进行开票
* @param invoice
* @param orders
* @return
*/
@Override
public String Invoicing(Invoice invoice, List<OrderData> orders) {
NNOpenSDK sdk = NNOpenSDK.getIntance();
// API方法名
String method = "nuonuo.electronInvoice.requestBilling";
String senid = UUID.randomUUID().toString().replace("-", "");
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Map<String, Object>> orderMap = new HashMap<>();
orderMap.put("order", map);
//购方信息
map.put("buyerName", invoice.getPurchaserName());
map.put("buyerTaxNum", invoice.getPurchaserTaxCode());
// map.put("buyerTel", invoice.getPurchaserPhone());
map.put("buyerAddress", invoice.getPurchaserAddressPhone());
map.put("buyerAccount", invoice.getPurchaserAccountBank());
map.put("buyerPhone", invoice.getPurchaserPhone());
map.put("email", invoice.getPurchaserEmail());
map.put("remark", invoice.getRemark());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//销方信息
map.put("orderNo", invoice.getOrderNo());
map.put("invoiceDate", dateFormat.format(invoice.getInvoiceDate()));
map.put("salerName", invoice.getSalerName());
map.put("salerTaxNum", invoice.getSalerTaxCode());
map.put("salerTel", invoice.getSalerPhone());
map.put("salerAddress", invoice.getSalerAddress());
map.put("salerAccount", invoice.getSalerAccountBank());
map.put("clerk", invoice.getIssuer());
map.put("payee", invoice.getPayee());
map.put("checker", invoice.getReviewer());
//其他设置
map.put("pushMode", String.valueOf(invoice.getPushMode()));
map.put("invoiceType", String.valueOf(invoice.getInvoiceType()));
//订单信息
ArrayList<Map<String, String>> arrayList = Lists.newArrayList();
map.put("invoiceDetail", arrayList);
for (OrderData order : orders) {
HashMap<String, String> mx = new HashMap<>();
mx.put("goodsName", invoice.getContent());
mx.put("num", String.valueOf(order.getNum()));
//单价含税标志,0:不含税,1:含税
mx.put("withTaxFlag", String.valueOf(order.getWithTaxFlag()));
mx.put("price", String.valueOf(order.getPrice()));
mx.put("taxRate", String.valueOf(order.getTaxRate()));
mx.put("unit", order.getUnit());
mx.put("specType", order.getOrderName());
arrayList.add(mx);
}
String token = getTokenFP();
String jsonString = JSON.toJSONString(orderMap);
//调用第三方接口
String result = sdk.sendPostSyncRequest(invoiceUrl, senid, appKey2, appSecret2, token, taxnum2, method, jsonString);
log.info("result={}", result);
//把字符串json数据转换为map
JSONObject jsonObject = JSON.parseObject(result);
Map<String, String> data = (Map<String, String>) jsonObject.get("result");
String invoiceSerialNum = data.get("invoiceSerialNum");
if (StringUtils.isBlank(invoiceSerialNum)) {
log.error("失败原因={}", result);
throw new BaseException("开票失败");
}
return invoiceSerialNum;
}
@Override
public List<Map<String, Object>> invoiceResult(String invoiceSerialNum) {
NNOpenSDK sdk = NNOpenSDK.getIntance();
// API方法名
String method = "nuonuo.electronInvoice.CheckEInvoice";
HashMap<String, List<String>> map = new HashMap<>();
ArrayList<String> arrayList = Lists.newArrayList();
arrayList.add(invoiceSerialNum);
map.put("invoiceSerialNum", arrayList);
String content = JSON.toJSONString(map);
// 唯一标识,由企业自己生成32位随机码
String senid = UUID.randomUUID().toString().replace("-", "");
String data = null;
try {
data = sdk.sendPostSyncRequest(queryUrl, senid, appKey2, appSecret2, getTokenFP(), taxnum2, method, content);
} catch (Exception e) {
e.printStackTrace();
}
if (data!=null){
try {
JSONObject jsonObject = JSON.parseObject(data);
List<Map<String, Object>> mapList = (List<Map<String, Object>>) jsonObject.get("result");
return mapList;
} catch (Exception e) {
throw new BaseException("发票请求流水号错误");
}
}
return null;
}
/**
* 获取极速开票token
*/
private String getTokenJS() {
return getToken(appKey, appSecret);
}
/**
* 获取诺诺发票token
*/
public String getTokenFP() {
return getToken(appKey2, appSecret2);
}
private String getToken(String appKey, String appSecret) {
String token = (String) redisTemplate.opsForValue().get(appKey);
if (StringUtils.isBlank(token)) {
String json = NNOpenSDK.getIntance().getMerchantToken(appKey, appSecret);
log.info("token={}", json);
JSONObject jsonObject = JSONObject.parseObject(json);
token = (String) jsonObject.get("access_token");
if (StringUtils.isBlank(token)) {
throw new BaseException("获取极速开票token失败");
}
redisTemplate.opsForValue().set(appKey, token, 23, TimeUnit.HOURS);
}
return token;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment