目录
1.cannot find declaration to go to
本项目是针对一个商城开发的 微信小程序及后台crm管理系统。包含微信小程序及PC管理系统。
开发框架:微信小程序-uniApp;前端管理系统-VUE;后台服务-SpringBoot。
CRM
设置全局请求参数
希望将本系统做成通用的CRM管理系统,添加了projectId参数。其他项目需要复用时,可以通过projectId参数进行区分。前提是数据库中所有业务表都需要记录project_id 字段。
前端通过请求过滤器,在header中添加参数。
service.interceptors.request.use(config => {
config.headers['projectId'] = localStorage.getItem('projectId');
return config
} error => {
// Do something with request error
console.log(error) // for debug
return Promise.reject(error)
})
服务端通过AOP将controller收到的请求拦截,取出header中的projectId,放入searchParam。
@Pointcut("execution(public * com.ys.crm.portal.manager.web.rest..*(..))")
public void controllerPointcut() {
}
private static final String PROJECT_ID_KEY ="projectId";
@Around("controllerPointcut()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
for(Object arg :args) {
if(arg instanceof BaseSearchParam) {
BaseSearchParam searchParam = (BaseSearchParam) arg;
//获取请求信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String projectId = request.getHeader(PROJECT_ID_KEY);
//如果对象本身未设置该字段且 header中传入了该字段则从head中插入到对象
if(ObjectUtils.isEmpty(searchParam.getProjectId()) && StringUtil.isNotNull(projectId)) {
searchParam.setProjectId(projectId);
}
}
}
return pjp.proceed();
}
修改数据表中字段的字符集
修改某张表的某个字段
ALTER TABLE mytable MODIFY COLUMN `content` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
mysql 插入表情符号 '\xF0\x9F\x8D\xB0</...' 报错的解决方法 - 疯狂008的个人页面 - OSCHINA - 中文开源技术交流社区
left join查询,一对多查询时主表数据重复
对于left join查询,一直浅显的认为只是以左表为主表,但在主从表关系为一对多时,会出现主表中的数据重复查询的问题。
可以使用group by 或 distinct 解决
1.group by
SELECT
a.id,
a.activity_name,
pic.id
FROM
activity a
LEFT JOIN act_picture pic ON a.id = pic.activity_id
GROUP BY
a.id
根据主表id进行分组,对应“多”的从表中,默认保留第一条数据(对应需求:取详情(主表)对应的的第一张图片(从表))
2.distinct
3.在select 中添加子查询
SELECT
a.id,
a.activity_name,
(SELECT img_url from act_picture where deleted = 0 and activity_id = a.id order by update_time desc limit 1) pic
FROM
activity a
GROUP BY
a.id
ORDER BY a.update_time desc
在select 之后from之前,添加子查询,子查询中添加过滤条件然后排序取值。
多环境同步数据库
一. Navicat
通过Navicat提供的工具,可以很方便的同步多个环境之间的数据库结构、数据。
实际使用中结构同步这一操作使用频率较高,主要用于开发、测试、生产等环境之间,在开发环境新增的功能,要先发布到测试环境,再同步到UAT 或生产环境。
分别选择连接和数据库,左边是比较新的数据库,需要将左边的表结构同步到右边。
点击比对之后会把两个数据库的结构进行比对,并生成相应的修改语句,单击相应的表,可以查看针对某张表的DDL语句,判断哪些表需要同步的,选中之后点击部署即可。
二. MySQLWorkbench
1.通过逆向工程 Datebase - Reverse Engineer 生成E-R 模型。
2.选择Datebase - Synchronize Model 选择要同步的数据库连接,选定对应的数据库、表。需要通过SSH隧道访问的可以切换Connection Method 选择Standard TCP / IP over SSH。
continue到选择Schemas 时,记得选源数据库,点击Override Target 再点下一步。
倒数第二步,会把即将对目标数据库做的修改SQL展示出来预览,可以点击执行,或拿出来在单独执行某条SQL进行修改。
前后端时间参数传递
为前端同事提供接口时,时间参数的传递总是最容易出现400错误的。所以为了降低前后端沟通成本,可以约定统一使用时间戳传递时间参数。
前端对于时间的格式可以千变万化,如:年月日的前后顺序、分隔符、时区等多种不同的表现形式;而前端框架中的组件,对于时间的格式,可能在某一个框架内部,是统一的,但是框架与框架之间是否统一就不一定咯。
显然让后端一个接口支持所有时间格式不是好的做法,因此我们选择使用可以表达任意时间,所有前端框架都会支持的时间戳,作为前后端传递时间的统一格式。
布尔类型参数传递
接口使用boolean/Boolean 类型接收参数时,除了可以正常传入true/ false 以外,还可以传入整数。整数0对应false,其余整数均为true。
tips:逻辑电路 ->
没有电压时,代表假,也就是0。
有电压时,而不管电压是多大,均代表真。
对应的,传入接口的数字只要不是0,服务端的boolean类型就会识别为true
Excel导入、导出
实现:使用Hutool 工具包
tips : hutool-5.x的poi-ooxml 版本需高于 4.1.2
<!-- hutool https://www.hutool.cn/docs/#/ -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<!-- hutool-5.x的poi-ooxml 版本需高于 4.1.2 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
小程序(服务端)
前期准备
1.小程序开发:AppSecret、开发者权限(用于获取AppId、token、设置IP白名单等操作)。
2.微信支付:商户Id、商户秘钥(v3支付需要v3支付秘钥)、商户证书(退款时验证)。
3.智慧商圈:
微信商圈:商圈申请、小程序积分插件、商圈商户号、V3秘钥、证书序列号
支付宝商圈:卡管套件安装包
微信支付
需求:开通微信支付。
实现:集成 bestPay 开发工具包。
踩坑记录:
1.开通支付能力
bestPay 调用的是微信的 统一下单接口(v2),需要开通JSAPI支付能力。由于第一时间进行开发时,直接根据微信官方文档调用接口,返回的 403 forbidden 错误码,官方文档也没有对应的说明,反复查询文档检查参数未果,转而集成了bestPay SDK。集成之后的错误提示清晰明了:
【微信统一支付】发起支付, returnCode != SUCCESS, returnMsg = 商户号该产品权限未开通,请前往商户平台>产品中心检查后重试
至此联系了小程序管理员,开通JSAPI支付能力之后继续开发。
2.支付回调
微信公众号推送
需求:小程序用户绑定结果、活动通知等需要通过微信向指定用户推送消息。
实现:通过公众号的模板消息实现。
踩坑记录:
1.unionId
原本打算使用订阅消息实现,但需要用户订阅之后才能发送,且小程序所属行业没有长期订阅模板,所以调整为通过公众号的模板消息实现。
用户入口在小程序,要在公众号推送消息就需要通过用户的unionId 获取到用户在公众号的openId,指定openId 发送模板消息。这里需要注意的是小程序与要推送消息的公众号必须属于同一主体,且绑定在同一个微信开放平台账号下,否则wx.login()获取的code无法换取unionId。
2.模板消息跳转小程序:
测试跳转时,发送模板消息报错"errcode":40165,"errmsg":"invalid weapp pagepath rid: 611dcc4c-422de372-62df2e28"。原因是小程序在开发时发布的是体验版,公众号跳转只能转到已经发布过的页面。网上说的将参数中的pagepath或改为page,可以成功推送消息,但本质上等于pagepath="",但无法跳转指定的页面,目前猜测是跳转默认页(小程序未发布)。
IDEA
1.cannot find declaration to go to
问题描述:早上打开IDEA ,两个定义常量的类无法通过ctrl+左键跳转,点击就提示cannot find ... ,且所有引用爆红提示 IDEA Cannot Resolve Symbol
解决方法:清除缓存 并重启 file -> Invalidate Chaches / Restart
2.maven依赖冲突
问题描述:依赖冲突,pom中未显示声明低版本依赖,需要通过mvn命令查出依赖树。
解决方法:https://blog.csdn.net/cm15835106905/article/details/107964359
在项目所在目录执行命令:
mvn dependency:tree>d:\tree.txt
将maven依赖树直接输出到D盘,根据groupId 和 artifactId查找冲突的依赖。然后使用<exclusions>标签排除引用。
一点小问题(O_O)?
1.BaseModel的用法
公司内部封装的统一返回格式BaseModel
public class BaseModel<T> implements Serializable {
private static final long serialVersionUID = 8868163258271605343L;
private String status = "200";
private String message = "操作成功";
private static final String errorMessage = "操作失败";
private T data;
private PageModel page;
... //get、set、构造方法等
}
需求:活动签到,根据用户是否参与了指定活动,需要分别返回两种数据结构。第一种是需要返回一个Long类型的ID,第二种需要返回一个对象。
但是BaseModel类提供的静态方法中,buildSuccess()不支持修改status,buildError()又不支持放入data。问了工作经验比较足的同事,说两种情况都属于操作成功,应该在返回的data中添加状态枚举,再分别添加一个Long和一个结果对象,根据不同的业务结果,为不同的字段赋值,这样方便前端进行请求拦截,对除了status=“200”以外的响应进行统一处理(弹框提示message)。
疑惑:我的想法是通过返回不同的status进行区分,利用泛型T可以放入任意类型的数据。如果只是为了判断响应结果,status为什么不用boolean类型(只需要判断true或false)?而且当前已经有了一种特殊处理,对于非默认错误码(300)417进行拦截。
思考:前端的响应拦截-> 应该对需要进行统一处理(弹框提示message)的默认错误码BaseModel.status = 300的响应进行拦截,其余一律放行。然后调用方在响应结果中判断,分别进行不同的处理。
if(response.data.status =='300'){
var errMsg = response.data.message?response.data.message:'请求失败'
Message.error({
message: errMsg,
type: 'error',
duration: 2 * 1000
});
return Promise.reject(errMsg);
}
return response.data;
MemberApi.login(this.loginParam).then(response =>{
load.close();
if(response.status == '200'){
//登录成功
}else if (response.status == '417'){
//未注册
}
}).catch(() => {
load.close();
});
如果不通过status进行区分,那封装的意义是什么?用了泛型的data 只是用于指定规定结构体来用吗?