Merge branch 'wx' into leng_dev

This commit is contained in:
冷冷 committed 2019-04-06 14:23:48 +08:00
commit 2cf5293eb8
113 files changed
+4046 -181

No files matched your search

+1 -1
View File
@@ -23,7 +23,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<artifactId>pigx-activiti</artifactId>
+5 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<artifactId>pigx-codegen</artifactId>
@@ -49,6 +49,10 @@
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-log</artifactId>
</dependency>
<!--代码生成模板引擎-->
<dependency>
<artifactId>velocity</artifactId>
@@ -17,8 +17,8 @@
package com.pig4cloud.pigx.codegen;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxFeignClients;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer;
import com.pig4cloud.pigx.common.swagger.annotation.EnablePigxSwagger2;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
@@ -0,0 +1,25 @@
package com.pig4cloud.pigx.codegen.config;
import com.pig4cloud.pigx.codegen.util.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author lengleng
* @date 2019-03-31
* <p>
* 动态数据源类
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 指定路由Key
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
@@ -0,0 +1,102 @@
package com.pig4cloud.pigx.codegen.config;
import com.mysql.cj.jdbc.Driver;
import com.pig4cloud.pigx.codegen.util.DataSourceConstant;
import com.zaxxer.hikari.HikariDataSource;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* @author lengleng
* @date 2019-03-31
* <p>
* 动态数据源切换配置
*/
@Slf4j
@Configuration
@AllArgsConstructor
public class DynamicDataSourceConfig implements TransactionManagementConfigurer {
private final Map<Object, Object> dataSourceMap = new HashMap<>(8);
private final DataSourceProperties dataSourceProperties;
private final StringEncryptor stringEncryptor;
@Bean("dynamicDataSource")
public DynamicDataSource dataSource() {
DynamicDataSource ds = new DynamicDataSource();
HikariDataSource cads = new HikariDataSource();
cads.setJdbcUrl(dataSourceProperties.getUrl());
cads.setDriverClassName(dataSourceProperties.getDriverClassName());
cads.setUsername(dataSourceProperties.getUsername());
cads.setPassword(dataSourceProperties.getPassword());
ds.setDefaultTargetDataSource(cads);
dataSourceMap.put(0, cads);
ds.setTargetDataSources(dataSourceMap);
return ds;
}
/**
* 组装默认配置的数据源,查询数据库配置
*/
@PostConstruct
public void init() {
DriverManagerDataSource dds = new DriverManagerDataSource();
dds.setUrl(dataSourceProperties.getUrl());
dds.setDriverClassName(dataSourceProperties.getDriverClassName());
dds.setUsername(dataSourceProperties.getUsername());
dds.setPassword(dataSourceProperties.getPassword());
List<Map<String, Object>> dbList = new JdbcTemplate(dds).queryForList(DataSourceConstant.QUERY_DS_SQL);
log.info("开始 -> 初始化动态数据源");
Optional.of(dbList).ifPresent(list -> list.forEach(db -> {
log.info("数据源:{}", db.get(DataSourceConstant.DS_NAME));
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(String.valueOf(db.get(DataSourceConstant.DS_JDBC_URL)));
ds.setDriverClassName(Driver.class.getName());
ds.setUsername((String) db.get(DataSourceConstant.DS_NAME));
String decPwd = stringEncryptor.decrypt((String) db.get(DataSourceConstant.DS_USER_PWD));
ds.setPassword(decPwd);
dataSourceMap.put(db.get(DataSourceConstant.DS_ROUTE_KEY), ds);
}));
log.info("完毕 -> 初始化动态数据源,共计 {} 条", dataSourceMap.size());
}
/**
* 重新加载数据源配置
*/
public Boolean reload() {
init();
DynamicDataSource dataSource = dataSource();
dataSource.setTargetDataSources(dataSourceMap);
dataSource.afterPropertiesSet();
return Boolean.FALSE;
}
@Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager();
}
}
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.codegen.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.codegen.entity.SysDatasourceConf;
import com.pig4cloud.pigx.codegen.service.SysDatasourceConfService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 数据源管理
*
* @author pigx code generator
* @date 2019-03-31 16:00:20
*/
@RestController
@AllArgsConstructor
@RequestMapping("/dsconf")
public class SysDsConfController {
private final SysDatasourceConfService sysDatasourceConfService;
/**
* 分页查询
*
* @param page 分页对象
* @param sysDatasourceConf 数据源表
* @return
*/
@GetMapping("/page")
public R getSysDatasourceConfPage(Page page, SysDatasourceConf sysDatasourceConf) {
return new R<>(sysDatasourceConfService.page(page, Wrappers.query(sysDatasourceConf)));
}
/**
* 查询全部数据源
*
* @return
*/
@GetMapping("/list")
public R list() {
return new R<>(sysDatasourceConfService.list());
}
/**
* 通过id查询数据源表
*
* @param id id
* @return R
*/
@GetMapping("/{id}")
public R getById(@PathVariable("id") Integer id) {
return new R<>(sysDatasourceConfService.getById(id));
}
/**
* 新增数据源表
*
* @param sysDatasourceConf 数据源表
* @return R
*/
@SysLog("新增数据源表")
@PostMapping
public R save(@RequestBody SysDatasourceConf sysDatasourceConf) {
return new R<>(sysDatasourceConfService.saveDsByEnc(sysDatasourceConf));
}
/**
* 修改数据源表
*
* @param sysDatasourceConf 数据源表
* @return R
*/
@SysLog("修改数据源表")
@PutMapping
public R updateById(@RequestBody SysDatasourceConf sysDatasourceConf) {
return new R<>(sysDatasourceConfService.updateDsByEnc(sysDatasourceConf));
}
/**
* 通过id删除数据源表
*
* @param id id
* @return R
*/
@SysLog("删除数据源表")
@DeleteMapping("/{id}")
public R removeById(@PathVariable Integer id) {
return new R<>(sysDatasourceConfService.removeById(id));
}
}
@@ -28,7 +28,6 @@ import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 代码生成器
@@ -46,11 +45,12 @@ public class SysGeneratorController {
* 列表
*
* @param tableName 参数集
* @param id 数据源编号
* @return 数据库表
*/
@GetMapping("/page")
public R getPage(Page page, String tableName) {
return new R<>(sysGeneratorService.getPage(page, tableName));
public R getPage(Page page, String tableName, Integer id) {
return new R<>(sysGeneratorService.getPage(page, tableName, id));
}
/**
@@ -60,7 +60,6 @@ public class SysGeneratorController {
@PostMapping("/code")
public void generatorCode(@RequestBody GenConfig genConfig, HttpServletResponse response) {
byte[] data = sysGeneratorService.generatorCode(genConfig);
response.reset();
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s.zip", genConfig.getTableName()));
response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
@@ -26,6 +26,10 @@ import lombok.Data;
*/
@Data
public class GenConfig {
/**
* 数据源ID
*/
private Integer id;
/**
* 包名
*/
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.codegen.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 数据源表
*
* @author pigx code generator
* @date 2019-03-31 16:00:20
*/
@Data
@TableName("sys_datasource_conf")
@EqualsAndHashCode(callSuper = true)
public class SysDatasourceConf extends Model<SysDatasourceConf> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId
private Integer id;
/**
* 名称
*/
private String name;
/**
* jdbcurl
*/
private String url;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 创建时间
*/
private LocalDateTime createDate;
/**
* 更新
*/
private LocalDateTime updateDate;
/**
* 删除标记
*/
@TableLogic
private String delFlag;
/**
* 租户ID
*/
private Integer tenantId;
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.codegen.entity.SysDatasourceConf;
/**
* 数据源表
*
* @author pigx code generator
* @date 2019-03-31 16:00:20
*/
public interface SysDatasourceConfMapper extends BaseMapper<SysDatasourceConf> {
}
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.codegen.entity.SysDatasourceConf;
/**
* 数据源表
*
* @author pigx code generator
* @date 2019-03-31 16:00:20
*/
public interface SysDatasourceConfService extends IService<SysDatasourceConf> {
/**
* 保存数据源并且加密
*
* @param sysDatasourceConf
* @return
*/
Boolean saveDsByEnc(SysDatasourceConf sysDatasourceConf);
/**
* 更新数据源
*
* @param sysDatasourceConf
* @return
*/
Boolean updateDsByEnc(SysDatasourceConf sysDatasourceConf);
}
@@ -39,8 +39,10 @@ public interface SysGeneratorService {
/**
* 分页查询表
*
* @param tableName 表名
* @param id 数据源ID
* @return
*/
IPage<List<Map<String, Object>>> getPage(Page page, String tableName);
IPage<List<Map<String, Object>>> getPage(Page page, String tableName, Integer id);
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.codegen.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.codegen.config.DynamicDataSourceConfig;
import com.pig4cloud.pigx.codegen.entity.SysDatasourceConf;
import com.pig4cloud.pigx.codegen.mapper.SysDatasourceConfMapper;
import com.pig4cloud.pigx.codegen.service.SysDatasourceConfService;
import lombok.AllArgsConstructor;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.stereotype.Service;
/**
* 数据源表
*
* @author pigx code generator
* @date 2019-03-31 16:00:20
*/
@Service
@AllArgsConstructor
public class SysDatasourceConfServiceImpl extends ServiceImpl<SysDatasourceConfMapper, SysDatasourceConf> implements SysDatasourceConfService {
private final DynamicDataSourceConfig dynamicDataSourceConfig;
private final StringEncryptor stringEncryptor;
/**
* 保存数据源并且加密
*
* @param sysDatasourceConf
* @return
*/
@Override
public Boolean saveDsByEnc(SysDatasourceConf sysDatasourceConf) {
sysDatasourceConf.setPassword(stringEncryptor.encrypt(sysDatasourceConf.getPassword()));
this.baseMapper.insert(sysDatasourceConf);
return dynamicDataSourceConfig.reload();
}
/**
* 更新数据源
*
* @param sysDatasourceConf
* @return
*/
@Override
public Boolean updateDsByEnc(SysDatasourceConf sysDatasourceConf) {
if (StrUtil.isNotBlank(sysDatasourceConf.getPassword())) {
sysDatasourceConf.setPassword(stringEncryptor.encrypt(sysDatasourceConf.getPassword()));
}
this.baseMapper.updateById(sysDatasourceConf);
return dynamicDataSourceConfig.reload();
}
}
@@ -21,8 +21,10 @@ import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.codegen.entity.GenConfig;
import com.pig4cloud.pigx.codegen.mapper.SysDatasourceConfMapper;
import com.pig4cloud.pigx.codegen.mapper.SysGeneratorMapper;
import com.pig4cloud.pigx.codegen.service.SysGeneratorService;
import com.pig4cloud.pigx.codegen.util.DynamicDataSourceContextHolder;
import com.pig4cloud.pigx.codegen.util.GenUtils;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
@@ -41,18 +43,20 @@ import java.util.zip.ZipOutputStream;
@Service
@AllArgsConstructor
public class SysGeneratorServiceImpl implements SysGeneratorService {
private final SysDatasourceConfMapper sysDatasourceConfMapper;
private final SysGeneratorMapper sysGeneratorMapper;
/**
* 分页查询表
*
* @param tableName 查询条件
* @param id
* @return
*/
@Override
public IPage<List<Map<String, Object>>> getPage(Page page, String tableName) {
return sysGeneratorMapper.queryList(page,tableName);
public IPage<List<Map<String, Object>>> getPage(Page page, String tableName, Integer id) {
DynamicDataSourceContextHolder.setDataSourceType(id);
return sysGeneratorMapper.queryList(page, tableName);
}
/**
@@ -63,6 +67,7 @@ public class SysGeneratorServiceImpl implements SysGeneratorService {
*/
@Override
public byte[] generatorCode(GenConfig genConfig) {
DynamicDataSourceContextHolder.setDataSourceType(genConfig.getId());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream);
@@ -0,0 +1,40 @@
package com.pig4cloud.pigx.codegen.util;
/**
* @author lengleng
* @date 2019-04-01
* <p>
* 数据源相关常量
*/
public interface DataSourceConstant {
/**
* 查询数据源的SQL
*/
String QUERY_DS_SQL = "select * from sys_datasource_conf where del_flag = 0";
/**
* 动态路由KEY
*/
String DS_ROUTE_KEY = "id";
/**
* 数据源名称
*/
String DS_NAME = "name";
/**
* jdbcurl
*/
String DS_JDBC_URL = "url";
/**
* 用户名
*/
String DS_USER_NAME = "username";
/**
* 密码
*/
String DS_USER_PWD = "password";
}
@@ -0,0 +1,26 @@
package com.pig4cloud.pigx.codegen.util;
import lombok.experimental.UtilityClass;
/**
* @author lengleng
* @date 2019-03-31
* <p>
* 保存上线文中数据库路由
*/
@UtilityClass
public class DynamicDataSourceContextHolder {
private final ThreadLocal<Integer> CONTEXT_HOLDER = new ThreadLocal<>();
public void setDataSourceType(Integer dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
public Integer getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
@@ -62,10 +62,6 @@
tableOption: tableOption
}
},
created() {
},
mounted: function () {
},
computed: {
...mapGetters(['permissions']),
permissionList() {
@@ -98,11 +94,7 @@
return delObj(row.$pk.lowerAttrName)
}).then(data => {
_this.tableData.splice(index, 1)
_this.$message({
showClose: true,
message: '删除成功',
type: 'success'
})
_this.$message.success('删除成功')
this.getList(this.page)
}).catch(function (err) {
})
@@ -117,11 +109,7 @@
handleUpdate: function (row, index, done) {
putObj(row).then(data => {
this.tableData.splice(index, 1, Object.assign({}, row))
this.$message({
showClose: true,
message: '修改成功',
type: 'success'
})
_this.$message.success('修改成功')
done()
this.getList(this.page)
})
@@ -135,11 +123,7 @@
handleSave: function (row, done) {
addObj(row).then(data => {
this.tableData.push(Object.assign({}, row))
this.$message({
showClose: true,
message: '添加成功',
type: 'success'
})
_this.$message.success('添加成功')
done()
this.getList(this.page)
})
+1 -1
View File
@@ -23,7 +23,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<artifactId>pigx-daemon-elastic-job</artifactId>
@@ -17,7 +17,7 @@ import java.time.LocalDateTime;
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("job_execution_log")
@TableName("JOB_EXECUTION_LOG")
public class ExecutionLog extends Model<ExecutionLog> {
private static final long serialVersionUID = 1L;
@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("job_status_trace_log")
@TableName("JOB_STATUS_TRACE_LOG")
public class StatusTraceLog extends Model<StatusTraceLog> {
private static final long serialVersionUID = 1L;
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<artifactId>pigx-monitor</artifactId>
@@ -36,7 +36,7 @@
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-core</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
+15
View File
@@ -0,0 +1,15 @@
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER wangiegie@gmail.com
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mkdir -p /pigx-mp-manager
WORKDIR /pigx-mp-manager
EXPOSE 5003
ADD ./pigx-visual/pigx-mp-manager/target/pigx-mp-manager.jar ./
CMD java -Djava.security.egd=file:/dev/./urandom -jar pigx-mp-manager.jar
+85
View File
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.8.0</version>
</parent>
<artifactId>pigx-mp-manager</artifactId>
<packaging>jar</packaging>
<description>微信公众号管理模块</description>
<dependencies>
<!--配置中心客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--数据操作-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-data</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--common-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-core</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-swagger</artifactId>
</dependency>
<!--安全模块-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-log</artifactId>
</dependency>
<!--微信依赖-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxFeignClients;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer;
import com.pig4cloud.pigx.common.swagger.annotation.EnablePigxSwagger2;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
/**
* @author lengleng
* @date 2019/03/25
* 代码生成模块
*/
@EnablePigxSwagger2
@SpringCloudApplication
@EnablePigxFeignClients
@EnablePigxResourceServer
public class PigxMpManagerApplication {
public static void main(String[] args) {
SpringApplication.run(PigxMpManagerApplication.class, args);
}
}
@@ -0,0 +1,21 @@
package com.pig4cloud.pigx.mp.builder;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public abstract class AbstractBuilder {
/**
* 构建返回微信消息报文
*
* @param content
* @param wxMessage
* @param service
* @return
*/
public abstract WxMpXmlOutMessage build(String content,
WxMpXmlMessage wxMessage, WxMpService service);
}
@@ -0,0 +1,24 @@
package com.pig4cloud.pigx.mp.builder;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public class ImageBuilder extends AbstractBuilder {
@Override
public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
WxMpService service) {
WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId(content)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
return m;
}
}
@@ -0,0 +1,22 @@
package com.pig4cloud.pigx.mp.builder;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public class TextBuilder extends AbstractBuilder {
@Override
public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
WxMpService service) {
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(content)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
return m;
}
}
@@ -0,0 +1,149 @@
package com.pig4cloud.pigx.mp.config;
import com.google.common.collect.Maps;
import com.pig4cloud.pigx.mp.handler.*;
import com.pig4cloud.pigx.mp.service.WxAccountService;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.stream.Collectors;
import static me.chanjar.weixin.common.api.WxConsts.*;
/**
* @author Binary Wang
* @author lengleng
* <p>
* 微信公众号基础配置,初始化配置
*/
@Configuration
public class WxMpConfiguration {
public static Map<String, WxMpMessageRouter> getRouters() {
return routers;
}
public static Map<String, WxMpService> getMpServices() {
return mpServices;
}
private WxAccountService accountService;
private LogHandler logHandler;
private NullHandler nullHandler;
private KfSessionHandler kfSessionHandler;
private StoreCheckNotifyHandler storeCheckNotifyHandler;
private LocationHandler locationHandler;
private MenuHandler menuHandler;
private MsgHandler msgHandler;
private UnsubscribeHandler unsubscribeHandler;
private SubscribeHandler subscribeHandler;
private ScanHandler scanHandler;
private RedisTemplate redisTemplate;
private static Map<String, WxMpMessageRouter> routers = Maps.newHashMap();
private static Map<String, WxMpService> mpServices = Maps.newHashMap();
@Autowired
public WxMpConfiguration(WxAccountService accountService, LogHandler logHandler, NullHandler nullHandler
, KfSessionHandler kfSessionHandler, StoreCheckNotifyHandler storeCheckNotifyHandler
, LocationHandler locationHandler, MenuHandler menuHandler
, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler
, SubscribeHandler subscribeHandler, ScanHandler scanHandler
, RedisTemplate pigxRedisTemplate) {
this.accountService = accountService;
this.logHandler = logHandler;
this.nullHandler = nullHandler;
this.kfSessionHandler = kfSessionHandler;
this.storeCheckNotifyHandler = storeCheckNotifyHandler;
this.locationHandler = locationHandler;
this.menuHandler = menuHandler;
this.msgHandler = msgHandler;
this.unsubscribeHandler = unsubscribeHandler;
this.subscribeHandler = subscribeHandler;
this.scanHandler = scanHandler;
this.redisTemplate = pigxRedisTemplate;
}
@PostConstruct
public void initServices() {
mpServices = accountService.list().stream().map(a -> {
WxMpInRedisConfigStorage configStorage = new WxMpInRedisConfigStorage(redisTemplate);
configStorage.setAppId(a.getAppid());
configStorage.setSecret(a.getAppsecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAeskey());
WxMpService service = new WxMpServiceImpl();
service.setWxMpConfigStorage(configStorage);
routers.put(a.getAppid(), this.newRouter(service));
return service;
}).collect(Collectors.toMap(s -> s.getWxMpConfigStorage().getAppId(), a -> a));
}
private WxMpMessageRouter newRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
// 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next();
// 接收客服会话管理事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
.handler(this.kfSessionHandler)
.end();
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
.handler(this.kfSessionHandler).end();
// 门店审核事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(WxMpEventConstants.POI_CHECK_NOTIFY)
.handler(this.storeCheckNotifyHandler).end();
// 自定义菜单事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(MenuButtonType.CLICK).handler(this.menuHandler).end();
// 点击菜单连接事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(MenuButtonType.VIEW).handler(this.nullHandler).end();
// 关注事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(EventType.SUBSCRIBE).handler(this.subscribeHandler)
.end();
// 取消关注事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(EventType.UNSUBSCRIBE)
.handler(this.unsubscribeHandler).end();
// 上报地理位置事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(EventType.LOCATION).handler(this.locationHandler)
.end();
// 接收地理位置消息
newRouter.rule().async(false).msgType(XmlMsgType.LOCATION)
.handler(this.locationHandler).end();
// 扫码事件
newRouter.rule().async(false).msgType(XmlMsgType.EVENT)
.event(EventType.SCAN).handler(this.scanHandler).end();
// 默认
newRouter.rule().async(false).handler(this.msgHandler).end();
return newRouter;
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.config;
import com.alibaba.ttl.TransmittableThreadLocal;
import lombok.experimental.UtilityClass;
/**
* @author lengleng
* @date 2019/03/27
* 微信上下文工具类
*/
@UtilityClass
public class WxMpContextHolder {
private final ThreadLocal<String> THREAD_LOCAL_APPID = new TransmittableThreadLocal<>();
/**
* TTL 设置appId
*
* @param appId
*/
public void setAppId(String appId) {
THREAD_LOCAL_APPID.set(appId);
}
/**
* 获取TTL中的appId
*
* @return
*/
public String getAppId() {
return THREAD_LOCAL_APPID.get();
}
public void clear() {
THREAD_LOCAL_APPID.remove();
}
}
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2018-2025, 云集汇通 All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the yunjihuitong.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*/
package com.pig4cloud.pigx.mp.config;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.enums.TicketType;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
/**
* @author lengleng
* @date 2019/03/26
* <p>
* 微信SDK redis扩展
*/
public class WxMpInRedisConfigStorage extends WxMpInMemoryConfigStorage {
private final static String ACCESS_TOKEN_KEY = "wechat:access_token_";
private final static String JSAPI_TICKET_KEY = "wechat:jsapi_ticket_";
private final static String CARDAPI_TICKET_KEY = "wechat:cardapi_ticket_";
private final RedisTemplate<String, String> redisTemplate;
private String accessTokenKey;
private String jsapiTicketKey;
private String cardapiTicketKey;
public WxMpInRedisConfigStorage(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 每个公众号生成独有的存储key
*
* @param appId
*/
@Override
public void setAppId(String appId) {
super.setAppId(appId);
this.accessTokenKey = ACCESS_TOKEN_KEY.concat(appId);
this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(appId);
this.cardapiTicketKey = CARDAPI_TICKET_KEY.concat(appId);
}
@Override
public String getAccessToken() {
return redisTemplate.opsForValue().get(this.accessTokenKey);
}
@Override
public boolean isAccessTokenExpired() {
return redisTemplate.getExpire(accessTokenKey) < 2L;
}
@Override
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
redisTemplate.opsForValue().set(accessTokenKey, accessToken, expiresInSeconds - 200, TimeUnit.SECONDS);
}
@Override
public void expireAccessToken() {
redisTemplate.expire(this.accessTokenKey, 0, TimeUnit.SECONDS);
}
@Override
public String getJsapiTicket() {
return redisTemplate.opsForValue().get(this.jsapiTicketKey);
}
@Override
public String getCardApiTicket() {
return redisTemplate.opsForValue().get(cardapiTicketKey);
}
@Override
public String getTicket(TicketType type) {
return redisTemplate.opsForValue().get(getTicketRedisKey(type));
}
@Override
public boolean isTicketExpired(TicketType type) {
return redisTemplate.getExpire(this.getTicketRedisKey(type)) < 2;
}
@Override
public synchronized void updateTicket(TicketType type, String jsapiTicket, int expiresInSeconds) {
redisTemplate.opsForValue().set(getTicketRedisKey(type), jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS);
}
@Override
public void expireTicket(TicketType type) {
redisTemplate.expire(getTicketRedisKey(type), 0, TimeUnit.SECONDS);
}
private String getTicketRedisKey(TicketType type) {
return String.format("wechat:ticket:key:%s:%s", this.appId, type.getCode());
}
}
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.service.WxAccountService;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
* 公众号账户
*
* @author lengleng
* @date 2019-03-26 22:07:53
*/
@RestController
@AllArgsConstructor
@RequestMapping("/wxaccount")
public class WxAccountController {
private final WxMpConfiguration wxMpConfiguration;
private final WxAccountService wxAccountService;
/**
* 分页查询
*
* @param page 分页对象
* @param wxAccount 公众号账户
* @return
*/
@GetMapping("/page")
public R getWxAccountPage(Page page, WxAccount wxAccount) {
return new R<>(wxAccountService.page(page, Wrappers.query(wxAccount)));
}
/**
* 通过id查询公众号账户
*
* @param id id
* @return R
*/
@GetMapping("/{id}")
public R getById(@PathVariable("id") Integer id) {
return new R<>(wxAccountService.getById(id));
}
/**
* 新增公众号账户
*
* @param wxAccount 公众号账户
* @return R
*/
@SysLog("新增公众号账户")
@PostMapping
@PreAuthorize("@pms.hasPermission('mp_wxaccount_add')")
public R save(@RequestBody WxAccount wxAccount) {
wxAccountService.save(wxAccount);
wxMpConfiguration.initServices();
return new R<>();
}
/**
* 修改公众号账户
*
* @param wxAccount 公众号账户
* @return R
*/
@SysLog("修改公众号账户")
@PutMapping
@PreAuthorize("@pms.hasPermission('mp_wxaccount_edit')")
public R updateById(@RequestBody WxAccount wxAccount) {
wxAccountService.updateById(wxAccount);
wxMpConfiguration.initServices();
return new R<>();
}
/**
* 通过id删除公众号账户
*
* @param id id
* @return R
*/
@SysLog("删除公众号账户")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('mp_wxaccount_del')")
public R removeById(@PathVariable Integer id) {
wxAccountService.removeById(id);
wxMpConfiguration.initServices();
return new R<>();
}
/**
* 生成公众号二维码
*
* @param appId
* @return
*/
@SysLog("生成公众号二维码")
@PostMapping("/qr/{appId}")
@PreAuthorize("@pms.hasPermission('mp_wxaccount_add')")
public R qr(@PathVariable String appId) {
return wxAccountService.generateQr(appId);
}
/**
* 获取公众号列表
*
* @return
*/
@GetMapping("/list")
public R list() {
return new R<>(wxAccountService.list());
}
/**
* 获取公众号接口数据
*
* @param appId 公众号
* @param interval 时间间隔
* @return
*/
@GetMapping("/statistics")
public R statistics(String appId, String interval) {
return wxAccountService.statistics(appId, interval);
}
}
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.mp.entity.WxAccountFans;
import com.pig4cloud.pigx.mp.service.WxAccountFansService;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
* 微信公众号粉丝管理
*
* @author lengleng
* @date 2019-03-26 22:08:08
*/
@RestController
@AllArgsConstructor
@RequestMapping("/wxaccountfans")
public class WxAccountFansController {
private final WxAccountFansService wxAccountFansService;
/**
* 分页查询
*
* @param page 分页对象
* @param wxAccountFans 微信公众号粉丝
* @return
*/
@GetMapping("/page")
public R getWxAccountFansPage(Page page, WxAccountFans wxAccountFans) {
return new R<>(wxAccountFansService.page(page, Wrappers.query(wxAccountFans)));
}
/**
* 通过id查询微信公众号粉丝
*
* @param id id
* @return R
*/
@GetMapping("/{id}")
public R getById(@PathVariable("id") Integer id) {
return new R<>(wxAccountFansService.getById(id));
}
/**
* 新增微信公众号粉丝
*
* @param wxAccountFans 微信公众号粉丝
* @return R
*/
@SysLog("新增微信公众号粉丝")
@PostMapping
@PreAuthorize("@pms.hasPermission('mp_wxaccountfans_add')")
public R save(@RequestBody WxAccountFans wxAccountFans) {
return new R<>(wxAccountFansService.save(wxAccountFans));
}
/**
* 修改微信公众号粉丝
*
* @param wxAccountFans 微信公众号粉丝
* @return R
*/
@SysLog("修改微信公众号粉丝")
@PutMapping
@PreAuthorize("@pms.hasPermission('mp_wxaccountfans_edit')")
public R updateById(@RequestBody WxAccountFans wxAccountFans) {
return new R<>(wxAccountFansService.updateById(wxAccountFans));
}
/**
* 通过id删除微信公众号粉丝
*
* @param id id
* @return R
*/
@SysLog("删除微信公众号粉丝")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('mp_wxaccountfans_del')")
public R removeById(@PathVariable Integer id) {
return new R<>(wxAccountFansService.removeById(id));
}
/**
* 同步粉丝
*
* @param appId
* @return
*/
@PostMapping("/sync/{appId}")
public R sync(@PathVariable String appId) {
return new R(wxAccountFansService.syncAccountFans(appId));
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.mp.entity.WxFansMsg;
import com.pig4cloud.pigx.mp.service.WxFansMsgService;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
* 微信粉丝消息管理
*
* @author lengleng
* @date 2019-03-27 20:45:27
*/
@RestController
@AllArgsConstructor
@RequestMapping("/wxfansmsg")
public class WxFansMsgController {
private final WxFansMsgService wxFansMsgService;
/**
* 分页查询
*
* @param page 分页对象
* @param wxFansMsg 消息
* @return
*/
@GetMapping("/page")
public R getWxFansMsgPage(Page page, WxFansMsg wxFansMsg) {
return new R<>(wxFansMsgService.page(page, Wrappers.query(wxFansMsg)));
}
/**
* 通过id查询消息
*
* @param id id
* @return R
*/
@GetMapping("/{id}")
public R getById(@PathVariable("id") Integer id) {
return new R<>(wxFansMsgService.getById(id));
}
/**
* 新增消息
*
* @param wxFansMsg 消息
* @return R
*/
@SysLog("新增消息")
@PostMapping
@PreAuthorize("@pms.hasPermission('mp_wxfansmsg_add')")
public R save(@RequestBody WxFansMsg wxFansMsg) {
return new R<>(wxFansMsgService.save(wxFansMsg));
}
/**
* 修改消息
*
* @param wxFansMsg 消息
* @return R
*/
@SysLog("修改消息")
@PutMapping
@PreAuthorize("@pms.hasPermission('mp_wxfansmsg_edit')")
public R updateById(@RequestBody WxFansMsg wxFansMsg) {
return new R<>(wxFansMsgService.updateById(wxFansMsg));
}
/**
* 通过id删除消息
*
* @param id id
* @return R
*/
@SysLog("删除消息")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('mp_wxfansmsg_del')")
public R removeById(@PathVariable Integer id) {
return new R<>(wxFansMsgService.removeById(id));
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.mp.entity.WxFansMsgRes;
import com.pig4cloud.pigx.mp.service.WxFansMsgResService;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
* 微信粉丝消息回复管理
*
* @author lengleng
* @date 2019-03-27 20:45:48
*/
@RestController
@AllArgsConstructor
@RequestMapping("/wxfansmsgres")
public class WxFansMsgResController {
private final WxFansMsgResService wxFansMsgResService;
/**
* 分页查询
*
* @param page 分页对象
* @param wxFansMsgRes 回复
* @return
*/
@GetMapping("/page")
public R getWxFansMsgResPage(Page page, WxFansMsgRes wxFansMsgRes) {
return new R<>(wxFansMsgResService.page(page, Wrappers.query(wxFansMsgRes)));
}
/**
* 通过id查询回复
*
* @param id id
* @return R
*/
@GetMapping("/{id}")
public R getById(@PathVariable("id") Integer id) {
return new R<>(wxFansMsgResService.getById(id));
}
/**
* 新增回复
*
* @param wxFansMsgRes 回复
* @return R
*/
@SysLog("新增回复")
@PostMapping
@PreAuthorize("@pms.hasPermission('mp_wxfansmsgres_add')")
public R save(@RequestBody WxFansMsgRes wxFansMsgRes) {
return wxFansMsgResService.saveAndSend(wxFansMsgRes);
}
/**
* 修改回复
*
* @param wxFansMsgRes 回复
* @return R
*/
@SysLog("修改回复")
@PutMapping
@PreAuthorize("@pms.hasPermission('mp_wxfansmsgres_edit')")
public R updateById(@RequestBody WxFansMsgRes wxFansMsgRes) {
return new R<>(wxFansMsgResService.updateById(wxFansMsgRes));
}
/**
* 通过id删除回复
*
* @param id id
* @return R
*/
@SysLog("删除回复")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('mp_wxfansmsgres_del')")
public R removeById(@PathVariable Integer id) {
return new R<>(wxFansMsgResService.removeById(id));
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.controller;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.mp.entity.WxMpMenu;
import com.pig4cloud.pigx.mp.service.WxMenuService;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
* 微信菜单管理
*
* @author lengleng
* @date 2019-03-27 20:45:18
*/
@RestController
@AllArgsConstructor
@RequestMapping("/wxmenu")
public class WxMenuController {
private final WxMenuService wxMenuService;
/**
* 分页查询
*
* @param page 分页对象
* @param WxMpMenu lengleng
* @return
*/
@GetMapping("/page")
public R getWxMpMenuPage(Page page, WxMpMenu WxMpMenu) {
return new R<>(wxMenuService.page(page, Wrappers.query(WxMpMenu)));
}
/**
* 通过appid查询微信菜单
*
* @param appId 公众号
* @return R
*/
@GetMapping("/{appId}")
public R getById(@PathVariable("appId") String appId) {
return wxMenuService.getByAppId(appId);
}
/**
* 新增微信菜单
*
* @param WxMpMenus 微信菜单列表
* @return R
*/
@SysLog("新增微信菜单")
@PostMapping("/{appId}")
@PreAuthorize("@pms.hasPermission('mp_wxmenu_add')")
public R save(@RequestBody JSONObject WxMpMenus, @PathVariable String appId) {
return new R(wxMenuService.save(WxMpMenus, appId));
}
/**
* 发布微信菜单
*
* @param appId 公众号
* @return R
*/
@SysLog("发布微信菜单")
@PutMapping("/{appId}")
@PreAuthorize("@pms.hasPermission('mp_wxmenu_push')")
public R updateById(@PathVariable String appId) {
return wxMenuService.push(appId);
}
}
@@ -0,0 +1,123 @@
package com.pig4cloud.pigx.mp.controller;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import com.pig4cloud.pigx.mp.config.WxMpContextHolder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
/**
* @author Binary Wang
* @author lengleng
* <p>
* 微信接入入口
*/
@Slf4j
@RestController
@RequestMapping("/{appId}/portal")
public class WxPortalController {
/**
* 微信接入校验处理
*
* @param appId 多公众号标志位
* @param signature 微信签名
* @param timestamp 时间戳
* @param nonce 随机数
* @param echostr 随机字符串
* @return
*/
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@PathVariable("appId") String appId,
@RequestParam(name = "signature", required = false) String signature,
@RequestParam(name = "timestamp", required = false) String timestamp,
@RequestParam(name = "nonce", required = false) String nonce,
@RequestParam(name = "echostr", required = false) String echostr) {
log.info("接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
final WxMpService wxService = WxMpConfiguration.getMpServices().get(appId);
if (wxService == null) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%d]的配置,请核实!", appId));
}
if (wxService.checkSignature(timestamp, nonce, signature)) {
return echostr;
}
return "非法请求";
}
/**
* 微信消息处理
*
* @param appId 多公众号标志位
* @param requestBody 请求报文体
* @param signature 微信签名
* @param encType 加签方式
* @param msgSignature 微信签名
* @param timestamp 时间戳
* @param nonce 随机数
* @return
*/
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@PathVariable("appId") String appId,
@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("openid") String openid,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
WxMpContextHolder.setAppId(appId);
final WxMpService wxService = WxMpConfiguration.getMpServices().get(appId);
log.info("接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[{}] ",
openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!wxService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
String out = null;
// 明文模式
if (StrUtil.isBlank(encType)) {
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
WxMpXmlOutMessage outMessage = WxMpConfiguration.getRouters().get(appId).route(inMessage);
if (outMessage != null) {
out = outMessage.toXml();
}
}
// aes加密模式
if (SecurityConstants.AES.equalsIgnoreCase(encType)) {
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(),
timestamp, nonce, msgSignature);
log.debug("消息解密后内容为:{} ", inMessage.toString());
WxMpXmlOutMessage outMessage = WxMpConfiguration.getRouters().get(appId).route(inMessage);
if (outMessage != null) {
out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
}
}
log.debug("组装回复信息:{}", out);
WxMpContextHolder.clear();
return out;
}
}
@@ -0,0 +1,37 @@
package com.pig4cloud.pigx.mp.controller;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author Edward
*/
@Controller
@RequestMapping("/wx/redirect/{appid}")
public class WxRedirectController {
@RequestMapping("/greet")
public String greetUser(@PathVariable String appid, @RequestParam String code, ModelMap map) {
WxMpService mpService = WxMpConfiguration.getMpServices().get(appid);
try {
WxMpOAuth2AccessToken accessToken = mpService.oauth2getAccessToken(code);
WxMpUser user = mpService.oauth2getUserInfo(accessToken, null);
map.put("user", user);
} catch (WxErrorException e) {
e.printStackTrace();
}
return "greet_user";
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 公众号账户
*
* @author lengleng
* @date 2019-03-26 22:07:53
*/
@Data
@TableName("t_wx_account")
@EqualsAndHashCode(callSuper = true)
public class WxAccount extends Model<WxAccount> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId
private Integer id;
/**
* 公众号名称
*/
private String name;
/**
* 公众号账户
*/
private String account;
/**
* 公众号appid
*/
private String appid;
/**
* 公众号密钥
*/
private String appsecret;
/**
* 公众号url
*/
private String url;
/**
* 公众号token
*/
private String token;
/**
* 加密密钥
*/
private String aeskey;
/**
* 二维码图片URL
*/
private String qrUrl;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 是否删除 -1:已删除 0:正常
*/
@TableLogic
private String delFlag;
/**
* 租户
*/
private Integer tenantId;
}
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 微信公众号粉丝
*
* @author lengleng
* @date 2019-03-26 22:08:08
*/
@Data
@TableName("t_wx_account_fans")
@EqualsAndHashCode(callSuper = true)
public class WxAccountFans extends Model<WxAccountFans> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId
private Integer id;
/**
* 用户标识
*/
private String openid;
/**
* 订阅状态,0未关注,1已关注
*/
private String subscribeStatus;
/**
* 订阅时间
*/
private LocalDateTime subscribeTime;
/**
* 昵称
*/
private String nickname;
/**
* 性别,1男,2女,0未知
*/
private String gender;
/**
* 语言
*/
private String language;
/**
* 国家
*/
private String country;
/**
* 省份
*/
private String province;
/**
* 城市
*/
private String city;
/**
* 头像地址
*/
private String headimgUrl;
/**
* 备注
*/
private String remark;
/**
* 微信公众号ID
*/
private Integer wxAccountId;
/**
* 微信公众号appid
*/
private String wxAccountAppid;
/**
* 微信公众号名
*/
private String wxAccountName;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 是否删除 -1:已删除 0:正常
*/
@TableLogic
private String delFlag;
}
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 微信消息
*
* @author lengleng
* @date 2019-03-27 20:45:27
*/
@Data
@TableName("t_wx_fans_msg")
@EqualsAndHashCode(callSuper = true)
public class WxFansMsg extends Model<WxFansMsg> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId
private Integer id;
/**
* 用户标识
*/
private String openid;
/**
* 昵称
*/
private String nickname;
/**
* 头像地址
*/
private String headimgUrl;
/**
* 微信账号ID
*/
private Integer wxAccountId;
/**
* 微信公众号appid
*/
private String wxAccountAppid;
/**
* 微信公众号名
*/
private String wxAccountName;
/**
* 消息ID
*/
private Long msgId;
/**
* 消息类型
*/
private String msgType;
/**
* 内容
*/
private String content;
/**
* 最近一条回复内容
*/
private String resContent;
/**
* 是否已回复
*/
private String isRes;
/**
* 微信素材ID
*/
private String mediaId;
/**
* 微信图片URL
*/
private String picUrl;
/**
* 本地图片路径
*/
private String picPath;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 是否删除 -1:已删除 0:正常
*/
@TableLogic
private String delFlag;
/**
* 租户
*/
private Integer tenantId;
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 微信粉丝消息回复表
*
* @author lengleng
* @date 2019-03-27 20:45:48
*/
@Data
@TableName("t_wx_fans_msg_res")
@EqualsAndHashCode(callSuper = true)
public class WxFansMsgRes extends Model<WxFansMsgRes> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId
private Integer id;
/**
* 粉丝消息ID
*/
private Integer fansMsgId;
/**
* 回复内容
*/
private String resContent;
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名
*/
private String userName;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 微信菜单表
*
* @author lengleng
* @date 2019-03-27 20:45:18
*/
@Data
@TableName("t_wx_menu")
@EqualsAndHashCode(callSuper = true)
public class WxMpMenu extends Model<WxMpMenu> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId
private Integer id;
/**
* 菜单
*/
private String menu;
/**
* 微信公众号ID
*/
private Integer wxAccountId;
/**
* 微信公众号appid
*/
private String wxAccountAppid;
/**
* 微信公众号名
*/
private String wxAccountName;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 是否删除 -1:已删除 0:正常
*/
@TableLogic
private String delFlag;
/**
* 是否发布 0 未发布 1 已发布
*/
private String pubFlag;
}
@@ -0,0 +1,9 @@
package com.pig4cloud.pigx.mp.handler;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public abstract class AbstractHandler implements WxMpMessageHandler {
}
@@ -0,0 +1,24 @@
package com.pig4cloud.pigx.mp.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class KfSessionHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
return null;
}
}
@@ -0,0 +1,34 @@
package com.pig4cloud.pigx.mp.handler;
import com.pig4cloud.pigx.mp.builder.TextBuilder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Slf4j
@Component
public class LocationHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
//上报地理位置事件
log.info("上报地理位置,纬度 : {},经度 : {},精度 : {}",
wxMessage.getLatitude(), wxMessage.getLongitude(), wxMessage.getPrecision());
//TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用
String content = "感谢反馈,您的的地理位置已收到!";
return new TextBuilder().build(content, wxMessage, null);
}
}
@@ -0,0 +1,62 @@
package com.pig4cloud.pigx.mp.handler;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.entity.WxFansMsg;
import com.pig4cloud.pigx.mp.service.WxAccountService;
import com.pig4cloud.pigx.mp.service.WxFansMsgService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author lengleng
* <p>
* 保存微信消息
*/
@Slf4j
@Component
@AllArgsConstructor
public class LogHandler extends AbstractHandler {
private final WxFansMsgService wxFansMsgService;
private final WxAccountService wxAccountService;
@Override
@SneakyThrows
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
log.debug("接收到请求消息,内容:{}", wxMessage.getContent());
WxAccount wxAccount = wxAccountService.getOne(Wrappers.<WxAccount>lambdaQuery()
.eq(WxAccount::getAccount, wxMessage.getToUser()));
WxMpUser wxmpUser = wxMpService.getUserService()
.userInfo(wxMessage.getFromUser());
WxFansMsg wxFansMsg = new WxFansMsg();
wxFansMsg.setOpenid(wxmpUser.getOpenId());
wxFansMsg.setNickname(wxmpUser.getNickname());
wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl());
wxFansMsg.setWxAccountId(wxAccount.getId());
wxFansMsg.setWxAccountName(wxAccount.getName());
wxFansMsg.setWxAccountAppid(wxAccount.getAppid());
wxFansMsg.setMsgType(wxMessage.getMsgType());
wxFansMsg.setMsgId(wxMessage.getMsgId());
wxFansMsg.setContent(wxMessage.getContent());
wxFansMsg.setTenantId(wxAccount.getTenantId());
wxFansMsgService.save(wxFansMsg);
log.debug("保存微信用户信息成功 {}", wxFansMsg);
return null;
}
}
@@ -0,0 +1,36 @@
package com.pig4cloud.pigx.mp.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class MenuHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) {
String msg = String.format("type:%s, event:%s, key:%s",
wxMessage.getMsgType(), wxMessage.getEvent(),
wxMessage.getEventKey());
if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) {
return null;
}
return WxMpXmlOutMessage.TEXT().content(msg)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
}
}
@@ -0,0 +1,49 @@
package com.pig4cloud.pigx.mp.handler;
import com.pig4cloud.pigx.mp.builder.TextBuilder;
import lombok.SneakyThrows;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class MsgHandler extends AbstractHandler {
@Override
@SneakyThrows
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) {
if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) {
//TODO 可以选择将消息保存到本地
}
//当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服
if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服")
&& weixinService.getKefuService().kfOnlineList()
.getKfOnlineList().size() > 0) {
return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE()
.fromUser(wxMessage.getToUser())
.toUser(wxMessage.getFromUser()).build();
}
//TODO 组装回复消息
String content = "收到信息内容:" + wxMessage.getContent();
return new TextBuilder().build(content, wxMessage, weixinService);
}
}
@@ -0,0 +1,24 @@
package com.pig4cloud.pigx.mp.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class NullHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
return null;
}
}
@@ -0,0 +1,24 @@
package com.pig4cloud.pigx.mp.handler;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class ScanHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map,
WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
// 扫码事件处理
return null;
}
}
@@ -0,0 +1,27 @@
package com.pig4cloud.pigx.mp.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 门店审核事件处理
*
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class StoreCheckNotifyHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
// TODO 处理门店审核事件
return null;
}
}
@@ -0,0 +1,72 @@
package com.pig4cloud.pigx.mp.handler;
import com.pig4cloud.pigx.mp.builder.TextBuilder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Slf4j
@Component
public class SubscribeHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) throws WxErrorException {
log.info("新关注用户 OPENID: " + wxMessage.getFromUser());
// 获取微信用户基本信息
try {
WxMpUser userWxInfo = weixinService.getUserService()
.userInfo(wxMessage.getFromUser(), null);
if (userWxInfo != null) {
// TODO 可以添加关注用户到本地数据库
}
} catch (WxErrorException e) {
if (e.getError().getErrorCode() == 48001) {
log.info("该公众号没有获取用户信息权限!");
}
}
WxMpXmlOutMessage responseResult = null;
try {
responseResult = this.handleSpecial(wxMessage);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
if (responseResult != null) {
return responseResult;
}
try {
return new TextBuilder().build("感谢关注", wxMessage, weixinService);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 处理特殊请求,比如如果是扫码进来的,可以做相应处理
*/
private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)
throws Exception {
//TODO
return null;
}
}
@@ -0,0 +1,29 @@
package com.pig4cloud.pigx.mp.handler;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Slf4j
@Component
public class UnsubscribeHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
String openId = wxMessage.getFromUser();
log.info("取消关注用户 OPENID: " + openId);
// TODO 可以更新本地数据库为取消关注状态
return null;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.mp.entity.WxAccountFans;
/**
* 微信公众号粉丝
*
* @author lengleng
* @date 2019-03-26 22:08:08
*/
public interface WxAccountFansMapper extends BaseMapper<WxAccountFans> {
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.mp.entity.WxAccount;
/**
* 公众号账户
*
* @author lengleng
* @date 2019-03-26 22:07:53
*/
public interface WxAccountMapper extends BaseMapper<WxAccount> {
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.mp.entity.WxFansMsg;
/**
* lengleng
*
* @author pigx code generator
* @date 2019-03-27 20:45:27
*/
public interface WxFansMsgMapper extends BaseMapper<WxFansMsg> {
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.mp.entity.WxFansMsgRes;
/**
* lengleng
*
* @author pigx code generator
* @date 2019-03-27 20:45:48
*/
public interface WxFansMsgResMapper extends BaseMapper<WxFansMsgRes> {
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.mp.entity.WxMpMenu;
/**
* lengleng
*
* @author pigx code generator
* @date 2019-03-27 20:45:18
*/
public interface WxMenuMapper extends BaseMapper<WxMpMenu> {
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.mp.entity.WxAccountFans;
/**
* 微信公众号粉丝
*
* @author lengleng
* @date 2019-03-26 22:08:08
*/
public interface WxAccountFansService extends IService<WxAccountFans> {
/**
* 同步指定公众号粉丝
*
* @param appId
* @return
*/
Boolean syncAccountFans(String appId);
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.mp.entity.WxAccount;
/**
* 公众号账户
*
* @author lengleng
* @date 2019-03-26 22:07:53
*/
public interface WxAccountService extends IService<WxAccount> {
/**
* 生成公众号二维码
*
* @param appId
* @return
*/
R generateQr(String appId);
/**
* 获取公众号统计数据
*
* @param appId
* @param interval
* @return
*/
R statistics(String appId, String interval);
}
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.mp.entity.WxFansMsgRes;
/**
* 微信消息回复业务
*
* @author lengleng
* @date 2019-03-27 20:45:48
*/
public interface WxFansMsgResService extends IService<WxFansMsgRes> {
/**
* 保存并发送回复
*
* @param wxFansMsgRes
* @return
*/
R saveAndSend(WxFansMsgRes wxFansMsgRes);
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.mp.entity.WxFansMsg;
/**
* 微信消息业务
*
* @author lengleng
* @date 2019-03-27 20:45:27
*/
public interface WxFansMsgService extends IService<WxFansMsg> {
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.mp.entity.WxMpMenu;
/**
* 微信菜单业务
*
* @author lengleng
* @date 2019-03-27 20:45:18
*/
public interface WxMenuService extends IService<WxMpMenu> {
/**
* 新增微信公众号按钮
*
* @param wxMenus json
* @param appId 公众号信息
* @return
*/
Boolean save(JSONObject wxMenus, String appId);
/**
* 发布到微信
*
* @param appId 公众号信息
* @return
*/
R push(String appId);
/**
* 通过appid 查询菜单信息
*
* @param appId
* @return
*/
R getByAppId(String appId);
}
@@ -0,0 +1,139 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.entity.WxAccountFans;
import com.pig4cloud.pigx.mp.mapper.WxAccountFansMapper;
import com.pig4cloud.pigx.mp.mapper.WxAccountMapper;
import com.pig4cloud.pigx.mp.service.WxAccountFansService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpUserService;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
/**
* @author lengleng
* @date 2019-03-26 22:08:08
* <p>
* 微信公众号粉丝
*/
@Slf4j
@Service
@AllArgsConstructor
public class WxAccountFansServiceImpl extends ServiceImpl<WxAccountFansMapper, WxAccountFans> implements WxAccountFansService {
private static final int SIZE = 100;
private final WxAccountMapper wxAccountMapper;
/**
* 获取公众号粉丝,生产建议异步
*
* @param appId
* @return
*/
@Async
@Override
@SneakyThrows
@Transactional(rollbackFor = Exception.class)
public Boolean syncAccountFans(String appId) {
WxAccount wxAccount = wxAccountMapper
.selectOne(Wrappers.<WxAccount>query().lambda()
.eq(WxAccount::getAppid, appId));
WxMpService wxMpService = WxMpConfiguration.getMpServices().get(appId);
WxMpUserService wxMpUserService = wxMpService.getUserService();
fetchUser(null, wxAccount, wxMpUserService);
return Boolean.TRUE;
}
/**
* 获取微信用户
*
* @param nextOpenid
* @param wxAccount
* @param wxMpUserService
* @throws WxErrorException
*/
private void fetchUser(String nextOpenid, WxAccount wxAccount, WxMpUserService wxMpUserService) throws WxErrorException {
WxMpUserList wxMpUserList = wxMpUserService.userList(nextOpenid);
// openId 分组 每组 100个 openid
CollUtil.split(wxMpUserList.getOpenids(), SIZE)
.stream().filter(CollUtil::isNotEmpty)
.forEach(openIdList -> {
log.info("开始批量获取用户信息 {}", openIdList);
List<WxAccountFans> wxAccountFansList = new ArrayList<>();
try {
wxMpUserService.userInfoList(openIdList).forEach(wxMpUser -> {
WxAccountFans wxAccountFans = baseMapper
.selectOne(Wrappers.<WxAccountFans>query().lambda()
.eq(WxAccountFans::getOpenid, wxMpUser.getOpenId()));
// 为空初始化一个新的对象
if (wxAccountFans == null) {
wxAccountFans = new WxAccountFans();
}
wxAccountFans.setOpenid(wxMpUser.getOpenId());
wxAccountFans.setSubscribeStatus(wxMpUser.getSubscribe() ? "1" : "0");
wxAccountFans.setSubscribeTime(LocalDateTime
.ofInstant(Instant.ofEpochMilli(wxMpUser.getSubscribeTime() * 1000L)
, ZoneId.systemDefault()));
wxAccountFans.setNickname(wxMpUser.getNickname());
wxAccountFans.setGender(String.valueOf(wxMpUser.getSex()));
wxAccountFans.setLanguage(wxMpUser.getLanguage());
wxAccountFans.setCountry(wxMpUser.getCountry());
wxAccountFans.setProvince(wxMpUser.getProvince());
wxAccountFans.setCity(wxMpUser.getCity());
wxAccountFans.setHeadimgUrl(wxMpUser.getHeadImgUrl());
wxAccountFans.setRemark(wxMpUser.getRemark());
wxAccountFans.setWxAccountId(wxAccount.getId());
wxAccountFans.setWxAccountAppid(wxAccount.getAppid());
wxAccountFans.setWxAccountName(wxAccount.getName());
wxAccountFansList.add(wxAccountFans);
});
this.saveOrUpdateBatch(wxAccountFansList);
log.info("批量插入用户信息完成 {}", openIdList);
} catch (WxErrorException e) {
log.error("批量同步粉丝失败", e);
}
});
// 如果nextOpenId 不为空,则继续获取
if (StrUtil.isNotBlank(wxMpUserList.getNextOpenid())) {
fetchUser(wxMpUserList.getNextOpenid(), wxAccount, wxMpUserService);
}
log.info("批量同步微信用户信息完成");
}
}
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.mapper.WxAccountMapper;
import com.pig4cloud.pigx.mp.service.WxAccountService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpDataCubeService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.datacube.WxDataCubeArticleResult;
import me.chanjar.weixin.mp.bean.datacube.WxDataCubeInterfaceResult;
import me.chanjar.weixin.mp.bean.datacube.WxDataCubeMsgResult;
import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserCumulate;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 公众号账户
*
* @author lengleng
* @date 2019-03-26 22:07:53
*/
@Slf4j
@Service
public class WxAccountServiceImpl extends ServiceImpl<WxAccountMapper, WxAccount> implements WxAccountService {
/**
* 生成公众号二维码
*
* @param appId
* @return
*/
@Override
public R generateQr(String appId) {
WxMpService wxMpService = WxMpConfiguration.getMpServices().get(appId);
try {
WxMpQrCodeTicket ticket = wxMpService.getQrcodeService().qrCodeCreateLastTicket(1);
String url = wxMpService.getQrcodeService().qrCodePictureUrl(ticket.getTicket());
WxAccount wxAccount = baseMapper
.selectOne(Wrappers.<WxAccount>query().lambda()
.eq(WxAccount::getAppid, appId));
wxAccount.setQrUrl(url);
baseMapper.updateById(wxAccount);
} catch (WxErrorException e) {
log.error(" 获取公众号二维码失败", e);
return R.builder().code(CommonConstants.FAIL).msg("更新公众号二维码失败").build();
}
return R.builder().build();
}
/**
* 获取公众号统计数据
*
* @param appId 公众号信息
* @param interval 时间间隔
* @return
*/
@Override
public R statistics(String appId, String interval) {
String[] split = interval.split(StrUtil.DASHED);
Date start = new Date(Long.parseLong(split[0]));
Date end = new Date(Long.parseLong(split[1]));
WxMpService wxMpService = WxMpConfiguration.getMpServices().get(appId);
WxMpDataCubeService cubeService = wxMpService.getDataCubeService();
List<List<Object>> result = new ArrayList<>();
try {
// 获取累计用户数据
List<Object> cumulateList = cubeService.getUserCumulate(start, end).stream()
.map(WxDataCubeUserCumulate::getCumulateUser)
.collect(Collectors.toList());
result.add(cumulateList);
// 获取用户分享数据
List<Object> shareList = cubeService.getUserShare(start, end).stream()
.map(WxDataCubeArticleResult::getShareCount)
.collect(Collectors.toList());
result.add(shareList);
// 获取消息发送概况数据
List<Object> upstreamList = cubeService.getUpstreamMsg(start, end).stream()
.map(WxDataCubeMsgResult::getMsgCount)
.collect(Collectors.toList());
result.add(upstreamList);
// 获取接口调用概况数据
List<WxDataCubeInterfaceResult> interfaceSummaryList = cubeService.getInterfaceSummary(start, end);
List<Object> interfaceList = interfaceSummaryList.stream()
.map(WxDataCubeInterfaceResult::getCallbackCount)
.collect(Collectors.toList());
result.add(interfaceList);
// 接口日期保存
List<Object> dateList = interfaceSummaryList.stream()
.map(WxDataCubeInterfaceResult::getRefDate)
.collect(Collectors.toList());
result.add(dateList);
} catch (WxErrorException e) {
log.error(" 获取公众号统计数据报错", e);
return R.builder().code(CommonConstants.FAIL).msg("获取公众号数据失败:" + e.getError().getErrorMsg()).build();
}
return R.builder().data(result).build();
}
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import com.pig4cloud.pigx.mp.entity.WxFansMsg;
import com.pig4cloud.pigx.mp.entity.WxFansMsgRes;
import com.pig4cloud.pigx.mp.mapper.WxFansMsgMapper;
import com.pig4cloud.pigx.mp.mapper.WxFansMsgResMapper;
import com.pig4cloud.pigx.mp.service.WxFansMsgResService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 微信消息回复业务
*
* @author lengleng
* @date 2019-03-27 20:45:48
*/
@Slf4j
@Service
@AllArgsConstructor
public class WxFansMsgResServiceImpl extends ServiceImpl<WxFansMsgResMapper, WxFansMsgRes> implements WxFansMsgResService {
private final WxFansMsgMapper wxFansMsgMapper;
/**
* 保存并发送回复
* <p>
* 1. 根据消息ID 查询消息的所属 公众号信息
* 2. 构建文本客服消息并发送
* 3. 完善回复消息并保存
* 4. 更新微信消息为已回复
*
* @param wxFansMsgRes
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R saveAndSend(WxFansMsgRes wxFansMsgRes) {
WxFansMsg wxFansMsg = wxFansMsgMapper.selectById(wxFansMsgRes.getFansMsgId());
String wxAccountAppid = wxFansMsg.getWxAccountAppid();
WxMpService wxMpService = WxMpConfiguration.getMpServices().get(wxAccountAppid);
try {
wxMpService.getKefuService().sendKefuMessage(WxMpKefuMessage
.TEXT()
.toUser(wxFansMsg.getOpenid())
.content(wxFansMsgRes.getResContent())
.build());
} catch (WxErrorException e) {
log.error("发送客服消息失败 ", e);
return R.builder().code(CommonConstants.FAIL).msg(e.getError().getErrorMsg()).build();
}
PigxUser user = SecurityUtils.getUser();
wxFansMsgRes.setUserId(user.getId());
wxFansMsgRes.setUserName(user.getUsername());
baseMapper.insert(wxFansMsgRes);
wxFansMsg.setIsRes("1");
wxFansMsgMapper.updateById(wxFansMsg);
return R.builder().build();
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.mp.entity.WxFansMsg;
import com.pig4cloud.pigx.mp.mapper.WxFansMsgMapper;
import com.pig4cloud.pigx.mp.service.WxFansMsgService;
import org.springframework.stereotype.Service;
/**
* 微信消息业务
*
* @author lengleng
* @date 2019-03-27 20:45:27
*/
@Service
public class WxFansMsgServiceImpl extends ServiceImpl<WxFansMsgMapper, WxFansMsg> implements WxFansMsgService {
}
@@ -0,0 +1,137 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.mp.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.mp.config.WxMpConfiguration;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.entity.WxMpMenu;
import com.pig4cloud.pigx.mp.mapper.WxAccountMapper;
import com.pig4cloud.pigx.mp.mapper.WxMenuMapper;
import com.pig4cloud.pigx.mp.service.WxMenuService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpMenuService;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 微信菜单业务
*
* @author lengleng
* @date 2019-03-27 20:45:18
*/
@Slf4j
@Service
@AllArgsConstructor
public class WxMenuServiceImpl extends ServiceImpl<WxMenuMapper, WxMpMenu> implements WxMenuService {
private static final String PUB_ED = "1";
private final WxAccountMapper wxAccountMapper;
/**
* 新增微信公众号按钮
*
* @param wxMenus json
* @param appId 公众号
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean save(JSONObject wxMenus, String appId) {
baseMapper.delete(Wrappers.<WxMpMenu>lambdaQuery()
.eq(WxMpMenu::getWxAccountAppid, appId));
WxAccount wxAccount = wxAccountMapper
.selectOne(Wrappers.<WxAccount>lambdaQuery()
.eq(WxAccount::getAppid, appId));
WxMpMenu wxMpMenu = new WxMpMenu();
wxMpMenu.setMenu(wxMenus.toStringPretty());
wxMpMenu.setWxAccountId(wxAccount.getId());
wxMpMenu.setWxAccountAppid(wxAccount.getAppid());
wxMpMenu.setWxAccountName(wxAccount.getName());
baseMapper.insert(wxMpMenu);
return Boolean.TRUE;
}
/**
* 发布到微信
*
* @param appId 公众号信息
* @return
*/
@Override
public R push(String appId) {
List<WxMpMenu> wxMpMenuList = baseMapper.selectList(Wrappers.<WxMpMenu>lambdaQuery()
.eq(WxMpMenu::getWxAccountAppid, appId));
if (CollUtil.isEmpty(wxMpMenuList)) {
return R.builder()
.code(CommonConstants.FAIL)
.msg("微信菜单配置未保存,不能发布").build();
}
WxMpMenu wxMpMenu = wxMpMenuList.get(0);
// 判断是否发布
if (PUB_ED.equals(wxMpMenu.getPubFlag())) {
return R.builder()
.code(CommonConstants.FAIL)
.msg("微信菜单配置已发布,不要重复发布").build();
}
WxMpService wxMpService = WxMpConfiguration.getMpServices().get(appId);
WxMpMenuService menuService = wxMpService.getMenuService();
// 给数据库保存的加一层
try {
menuService.menuCreate(wxMpMenu.getMenu());
} catch (WxErrorException e) {
log.error("发布微信菜单失败", e.getError().getErrorMsg());
return R.builder()
.code(CommonConstants.FAIL)
.msg(e.getError().getErrorMsg()).build();
}
//更新菜单发布标志
wxMpMenu.setPubFlag(PUB_ED);
baseMapper.updateById(wxMpMenu);
return R.builder().build();
}
/**
* 通过appid 查询菜单信息
*
* @param appId
* @return
*/
@Override
public R getByAppId(String appId) {
List<WxMpMenu> wxMpMenuList = baseMapper.selectList(Wrappers.<WxMpMenu>lambdaQuery()
.eq(WxMpMenu::getWxAccountAppid, appId));
if (CollUtil.isEmpty(wxMpMenuList)) {
return R.builder().build();
}
return R.builder().data(wxMpMenuList.get(0).getMenu()).build();
}
}
@@ -0,0 +1,25 @@
server:
port: 6000
spring:
application:
name: @artifactId@
#配置中心
cloud:
config:
fail-fast: true
name: ${spring.application.name}
profile: ${spring.profiles.active}
discovery:
enabled: true
service-id: pigx-config
profiles:
active: dev
# 注册中心配置
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://pig:pig@pigx-eureka:8761/eureka/
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.mp.mapper.WxAccountFansMapper">
<resultMap id="wxAccountFansMap" type="com.pig4cloud.pigx.mp.entity.WxAccountFans">
<id property="id" column="id"/>
<result property="openid" column="openid"/>
<result property="subscribeStatus" column="subscribe_status"/>
<result property="subscribeTime" column="subscribe_time"/>
<result property="nickname" column="nickname"/>
<result property="gender" column="gender"/>
<result property="language" column="language"/>
<result property="country" column="country"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="headimgUrl" column="headimg_url"/>
<result property="remark" column="remark"/>
<result property="wxAccountId" column="wx_account_id"/>
<result property="wxAccountAppid" column="wx_account_appid"/>
<result property="wxAccountName" column="wx_account_name"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
</mapper>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.mp.mapper.WxAccountMapper">
<resultMap id="wxAccountMap" type="com.pig4cloud.pigx.mp.entity.WxAccount">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="account" column="account"/>
<result property="appid" column="appid"/>
<result property="appsecret" column="appsecret"/>
<result property="url" column="url"/>
<result property="token" column="token"/>
<result property="aeskey" column="aeskey"/>
<result property="qrUrl" column="qr_url"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
</mapper>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.mp.mapper.WxFansMsgMapper">
<resultMap id="wxFansMsgMap" type="com.pig4cloud.pigx.mp.entity.WxFansMsg">
<id property="id" column="id"/>
<result property="openid" column="openid"/>
<result property="nickname" column="nickname"/>
<result property="headimgUrl" column="headimg_url"/>
<result property="wxAccountId" column="wx_account_id"/>
<result property="wxAccountName" column="wx_account_name"/>
<result property="wxAccountAppid" column="wx_account_appid"/>
<result property="msgType" column="msg_type"/>
<result property="content" column="content"/>
<result property="resContent" column="res_content"/>
<result property="isRes" column="is_res"/>
<result property="mediaId" column="media_id"/>
<result property="picUrl" column="pic_url"/>
<result property="picPath" column="pic_path"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
</mapper>
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.mp.mapper.WxFansMsgResMapper">
<resultMap id="wxFansMsgResMap" type="com.pig4cloud.pigx.mp.entity.WxFansMsgRes">
<id property="id" column="id"/>
<result property="fansMsgId" column="fans_msg_id"/>
<result property="resContent" column="res_content"/>
<result property="userId" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
</mapper>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.mp.mapper.WxMenuMapper">
</mapper>
+1 -1
View File
@@ -29,7 +29,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<dependencies>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<artifactId>pigx-tx-manager</artifactId>
+1 -1
View File
@@ -22,7 +22,7 @@
<parent>
<artifactId>pigx-visual</artifactId>
<groupId>com.pig4cloud</groupId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+2 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx</artifactId>
<version>2.7.0</version>
<version>2.8.0</version>
</parent>
<artifactId>pigx-visual</artifactId>
@@ -37,6 +37,7 @@
<module>pigx-daemon-elastic-job</module>
<module>pigx-daemon-quartz</module>
<module>pigx-monitor</module>
<module>pigx-mp-manager</module>
<module>pigx-sso-client-demo</module>
<module>pigx-tx-manager</module>
<module>pigx-zipkin</module>