Commit 7ac0726a authored by 赵文静's avatar 赵文静

上传

parent 6fc1e791
tencent-nlu-parse.exe
main.exe
.vscode/launch.json
__debug_*.exe
c2c.http
reqT0.http
reqT1.http
# 更新日志
## [v1.1.47] - 2025年2月24日20:25:28
### 新增
* 新增 local ip.
## [v1.1.46] - 2025年2月22日11:13:37
### 新增
* 新增 CGO_ENABLED=0 字段
## [v1.1.45] - 2025年2月21日15:56:07
### 新增
* 新增 log service 字段
## [v1.1.44-beta.1] - 2024年8月5日11:36:07
### 新增
* 新增 "gletkt_xlcj-1245258905417052160","ceshi-1149914105856290816","geliyoupingkongtiao-1289106967398617088","jhdpbxzswd-1364089899207012352","lxtest-1697504067777118921","lxtest-1697504245166677379","lchat-1736631343458063755" 的技能解析
## [v1.1.43-beta.28] - 2024年3月20日17:24:15
### 变更
* fm 细分码87654321 fm过滤m3u8 更改判断规则
## [v1.1.43-beta.27] - 2024年3月20日15:58:34
### 变更
* 腾讯切换正式环境
## [v1.1.43-beta.26] - 2024年3月20日10:47:31
### 变更
* 字段冲突 type更改为newsType
## [v1.1.43-beta.25] - 2024年3月4日16:44:11
### 删除
* 删除urltype判断逻辑
## [v1.1.43-beta.24] - 2024年3月4日15:57:55
### 变更
* appkey"a8814080829e11eda5c8e38bb1008e50" mid"10f05" 特殊处理同987654321设备一致
## [v1.1.43-beta.23] - 2024年3月4日15:15:19
### 变更
* appkey"a8814080829e11eda5c8e38bb1008e50" mid"10f05" 特殊处理同987654321设备一致
## [v1.1.43-beta.22] - 2024年2月1日17:17:05
### 修复
* 故事 title不存在则从selfData里找
## [v1.1.43-beta.21] - 2024年1月24日15:11:11
### 修复
* 旧版 修复闹钟不同步的问题
## [v1.1.43-beta.20] - 2024年1月10日15:06:14
### 变更
* 旧版 屏蔽音乐的"search_prev_song"意图
## [v1.1.43-beta.19] - 2023年12月22日10:04:48
### 变更
* 旧版 更改baike获取回复语的方式
### 新增
* 流式 新增mchat的解析
## [v1.1.43-beta.18] - 2023年11月30日14:31:38
### 新增
* 增加对提醒native command指令的支持
## [v1.1.43-beta.17] - 2023年11月29日09:56:13
### 新增
* 新增homeId
## [v1.1.43-beta.16] - 2023年11月28日10:39:22
### 新增
* tencentWs 接入闹钟提醒技能
* 修复mid mac 未赋值的问题
## [v1.1.43-beta.15] - 2023年11月23日11:15:13
### 变更
* OtherDomainWs 的回复语策略
## [v1.1.43-beta.14] - 2023年11月23日10:24:06
### 变更
* OtherDomainWs 的回复语策略
## [v1.1.43-beta.13] - 2023年11月23日10:24:06
### 变更
* OtherDomainWs 的回复语策略
## [v1.1.43-beta.12] - 2023年11月22日14:22:55
### 新增
* 增加技能解析
## [v1.1.43-beta.11] - 2023年11月16日10:54:44
### 新增
* 增加注释
## [v1.1.43-beta.10] - 2023年11月10日09:45:08
### 变更
* 直播类型链接过滤逻辑增加mid midType限制
## [v1.1.43-beta.9] - 2023年11月8日10:21:15
### 新增
* 增加直播类型链接的过滤逻辑, 过滤后资源列表为空时返回回复语"小格没有找到相关的资源哦"
## [v1.1.43-beta.8] - 2023年10月23日14:00:31
### 新增
* tencent wss 增加geography_kbqa general_question_answering htwhys_qa_pairs invention_qa_pairs 的解析
## [v1.1.43-beta.7] - 2023年10月23日14:00:31
### 变更
* tencent wss 增加language字段
## [v1.1.43-beta.6] - 2023年10月9日15:14:46
### 变更
* tencent wss 链接地址从consul env中获取
## [v1.1.43-beta.5] - 2023年10月9日14:05:08
### 变更
* tencent wss 链接地址从consul env中获取
## [v1.1.43-beta.4] - 2023年9月27日10:54:13
### 新增
* 新增 llm domain 的解析
## [v1.1.43-beta.3] - 2023年9月27日10:33:14
### 删除
* 去掉特殊处理(特殊处理部分设备切换到腾讯大语言模型bot)
## [v1.1.43-beta.2] - 2023年9月27日09:58:38
### 新增
* 特殊处理部分设备切换到腾讯大语言模型bot
## [v1.1.43-beta.1] - 2023年9月18日10:40:16
### 新增
* 腾讯流式接口 请求增加Location.City字段
* 增加RewrittenQuery字段
## [v1.1.42-beta.2] - 2023年9月18日10:40:16
### 修复
* search_ancientpoem_meaning responseText转义字符引发json解析错误
## [v1.1.42-beta.1] - 2023年9月13日14:51:14
### 修复
* search_ancientpoem_meaning responseText转义字符引发json解析错误
## [v1.1.41-beta.4] - 2023年8月3日14:08:56
### 修复
* 修复鉴权服务挂掉时引发的错误
## [v1.1.41-beta.3] - 2023年7月27日17:18:03
### 修复
* 修复鉴权服务挂掉时引发的错误
### 变更
* dmsdk status为1时也获取dsn
## [v1.1.41-beta.2] - 2023年7月27日11:03:12
### 变更
* 更改账号绑定\过期的提示话术
* 完善log输出
## [v1.1.41-beta.1] - 2023年7月26日15:37:48
### 变更
* 取消globalCtrl下'确定' '取消' 的协议转换
## [v1.1.40-beta.40] - 2023年6月29日14:11:48
### 变更
* SessionComplete字段缺失问题
## [v1.1.40-beta.39] - 2023年6月25日10:59:37
### 变更
* 查询闹钟和提醒时, 内容过多就进行截断, 按照句子截断
## [v1.1.40-beta.38] - 2023年6月21日15:24:46
### 变更
* 优化 日志字段
## [v1.1.40-beta.37] - 2023年6月21日11:51:00
### 新增
* 增加 腾讯wss 的接入方式
* 增加 流式grpc接口TencentNluParseStream
* 增加 日志字段
* 增加 readme对查票接口的说明
## [v1.1.40-beta.36] - 2023年6月16日16:10:43
### 变更
* 解决weather回复语带有转义字符导致json解析失败的问题
## [v1.1.40-beta.35] - 2023年6月16日11:28:26
### 变更
* 增加日志字段
* 增加help domain的解析, 使用固定的回复
## [v1.1.40-beta.34] - 2023年6月12日15:33:00
### 变更
* 增加日志字段
## [v1.1.40-beta.33] - 2023年6月12日14:17:00
### 变更
* 增加日志字段
## [v1.1.40-beta.32] - 2023年6月12日13:36:44
### 变更
* 增加日志字段
## [v1.1.40-beta.31] - 2023年6月12日10:48:21
### 变更
* 增加日志字段
* news 回复语修改
* tencentV2 票据字段的校验及默认字段
## [v1.1.40-beta.30] - 2023年6月5日10:46:46
### 变更
* 从环境变量读取腾讯的配置信息
## [v1.1.40-beta.29] - 2023年5月17日09:22:17
### 变更
* 删除音乐资源扩充的代码逻辑
## [v1.1.40-beta.28] - 2023年5月11日09:36:58
### 变更
* 音乐资源扩充到30首, 分组排序方便终端测试
## [v1.1.40-beta.27] - 2023年5月10日13:51:52
### 变更
* 音乐资源扩充到20首用于测试
## [v1.1.40-beta.26] - 2023年5月10日10:37:31
### 变更
* 音乐资源扩充到30首用于测试
* 增加音乐数目限制逻辑(mid非11011机型)
## [v1.1.40-beta.25] - 2023年5月9日17:18:10
### 变更
* dmsdk status==0时 使用其返回的appkey accessToken
## [v1.1.40-beta.24] - 2023年5月4日13:50:31
### 变更
* TokenSearch 使用增加mid vender字段查询dsn
## [v1.1.40-beta.23] - 2023年4月27日15:36:54
### 变更
* 腾讯v2 增加世界之最的解析
* 腾讯v2 古诗词鉴赏和意思, 拼接增加":"
## [v1.1.40-beta.22] - 2023年4月25日16:17:00
### 变更
* 腾讯v2 古诗domain中, 查询古诗含义, 查询古诗鉴赏, 拼接textContent 和 Text 进行返回
## [v1.1.40-beta.21] - 2023年4月24日13:59:24
### 变更
* 腾讯v2 增加mchat的解析
## [v1.1.40-beta.20] - 2023年4月19日11:16:51
### 变更
* 腾讯v2网关协议变更 auth字段放在http Header中
## [v1.1.40-beta.19] - 2023年4月14日11:27:34
### 变更
* 协议增加tRequestId字段
* 腾讯v2 增加requestid参数
## [v1.1.40-beta.18] - 2023年4月10日13:58:07
### 变更
* 腾讯v2 增加globalctrl3技能的解析
## [v1.1.40-beta.17] - 2023年4月4日17:18:55
### 变更
* 腾讯v2 不再填充guid字段
## [v1.1.40-beta.16] - 2023年4月4日15:42:13
### 新增
* 新增guid字段
## [v1.1.40-beta.15] - 2023年3月28日15:04:41
### 新增
* tencentV2 screen_control volumectrl3 music
### 修复
* tencentV2 guid的生成方式与旧接口的生成方式进行统一
## [v1.1.40-beta.14] - 2023年3月27日10:36:45
### 修复
* tencentV2 weather domain 的 text 校验
## [v1.1.40-beta.13] - 2023年3月27日10:03:54
### 修复
* tencentV2 weather domain 的 selfData 解析出错
## [v1.1.40-beta.12] - 2023年3月20日17:05:16
### 新增
* 增加鉴权信息字段, 腾讯v2 优先使用被传过来的鉴权信息
## [v1.1.40-beta.11] - 2023年3月17日09:06:01
### 修复
* 腾讯v2 newsdomain response_text 回复语过长, 替换成固定回复: "为你找到以下新闻:"
## [v1.1.40-beta.10] - 2023年3月16日17:24:16
### 修复
* 删除resType 字段的获取
## [v1.1.40-beta.9] - 2023年3月16日11:48:10
### 新增
* 腾讯v2接口 新增 Command字段透传
## [v1.1.40-beta.8] - 2023年3月15日15:44:38
### 新增
* 腾讯v2接口 新增 default domain
## [v1.1.40-beta.7] - 2023年3月15日15:24:10
### 变更
* 腾讯v2接口ip赋值更改为 Header X-Forwarded-For
## [v1.1.40-beta.6] - 2023年3月15日14:37:59
### 变更
* 细分码存在非十进制数字的情况, 将 midType 重命名为vender, 相关的proto协议及数据库表进行更新
## [v1.1.40-beta.5] - 2023年3月13日10:43:56
### 新增
* 增加服务发现没有找到对应服务的校验机制
### 变更
* weather 删除mid限制 删除selfData的返回
* chengyu 取消转为chat返回
## [v1.1.40-beta.4] - 2023年3月9日19:01:11
### 新增
* 模拟重定向
## [v1.1.40-beta.3] - 2023年3月3日08:49:51
### 新增
* 股票 星座 笑话 闲聊 体育 电台 古诗 新闻 节假日
## [v1.1.40-beta.2] - 2023年2月28日15:55:28
### 变更
* midAppKey 表增加midtype urltype字段, 读取进行兼容
* 请求协议增加midtype字段
* 增加新旧bot(接口)的访问逻辑, 指定的mid midtype(细分码)会访问新接口新bot
* 增加腾讯新接口的访问方式
* 新接口 天气 和 闲聊 技能的解析逻辑
## [v1.1.40-beta] - 2023年1月15日11:02:59
### 优化
* grpc请求增加 grpc.WithNoProxy()
## [v1.1.39] - 2023年1月12日11:08:53
### 修复
* 修复仅支持中英文翻译的判断逻辑
## [v1.1.38] - 2023年1月12日10:40:50
### 优化
* 变更consul服务信息获取方式
* 不再兼容从dmsk获取dsn的http接口
## [v1.1.37] - 2023年1月12日09:56:24
### 新增
* 增加middleware中间件对panic捕获
## [v1.1.36] - 2022年11月18日17:37:12
### 修复
* replyWithChat() response_text中含有特殊字符(\n)导致的解析错误问题
### 新增
* 对翻译语言进行了限制(测试)
## [v1.1.35] - 2022年10月28日10:23:16
### 变更
* newsDomain 取消replay_present不支持的回复语
## [v1.1.34] - 2022年10月21日11:14:33
### 修复
* poemDomain response_text中包含需转义的字符串导致json字符串无法加载的问题
## [v1.1.33] - 2022年10月14日14:19:37
### 变更
* poemDomain 对 "search_ancientpoem_meaning", "search_ancientpoem_appreciation", "search_ancientpoem_chains"意图 response_text进行拼接
## [v1.1.32] - 2022年9月14日19:17:35
### 变更
* poemDomain responst_text 为空时使用textContent
## [v1.1.31] - 2022年9月13日14:40:58
### 变更
* dislike的协议转换变更为next
## [v1.1.30] - 2022年9月5日09:31:12
### 修复
* fmDomain缺少mediaId
### 新增
* music 增加playmore dislike的协议转换
* poem 增加next的协议转换
* fm 增加playmore next prev 的协议转换
## [v1.1.29] - 2022年8月17日09:14:51
### 优化
* 优化consul client初始化方式
### 新增
* 增加对 globalctrl.turn_mid 协议转换
## [v1.1.28] - 2022年8月11日13:00:28
### 优化
* 优化通过consul获取host的方式
## [v1.1.27] - 2022年6月20日15:00:12
### 优化
* 天气技能 对内存不受限设备返回更多的天气数据
## [v1.0.15.1] - 2022年6月9日10:54:57
### 新增
- 天气技能 对无内存限制的设备返回更多的天气数据(vp柜机反馈缺少 温度,湿度,紫外线,七天天气数据,24小时温度数据)
### 说明
- tag v1.0.15 对应CHANGELOG.md文档中的 v1.0.9
- v1.0.15.1 表示在v1.0.15正式服版本基础上的修改
## [v1.1.26] - 2022年5月23日14:38:30
### 新增
* 天气技能 对有屏设备返回更多的天气数据(vp柜机反馈缺少 温度,湿度,紫外线,七天天气数据,24小时温度数据)
* config.yaml 配置文件 增加注释
## [v1.1.25] - 2022年5月23日10:17:48
### 优化
* 禅道 #64 对 music 下指定意图(pause pause_v2 next resume replay stop exit )进行协议转换
* 禅道 #71 对 fm 下指定意图(change)进行支持, 对指定意图(pause exit stop)进行协议转换
## [v1.1.24] - 2022年4月28日10:13:28
### 修复
* 未能从dmsdk查票服务获取到Appkey AccessToken 时 通过mid去查AppKey AccessToken
## [v1.1.23] - 2022年4月21日10:13:28
### 修复
* 更新docker file
## [v1.1.22] - 2022年4月19日10:13:28
### 修复
* 更新docker file
## [v1.1.21] - 2022年3月30日11:25:14
### 新增
* 请求叮当服务新增经纬度信息
## [v1.1.20] - 2022年3月25日15:26:40
### 新增
* 配置文件新增 11f06 10f05 相关的配置
### 修复
* 删除v1.1.18 "设备差异化的配置写死" 临时写死的逻辑
## [v1.1.19] - 2022年3月25日09:29:30
### 修复
* alarmDomain 返回字段格式问题
* 优化consul实时获取服务消息
## [v1.1.18] - 2022年3月22日17:38:36
### 新增
* 设备差异化的配置写死
### 修复
* 菜谱协议更改为listItems
* 优化http鉴权
## [v1.1.17] - 2022年3月4日15:11:00
### 新增
* dmsdk grpc服务不存在时则使用http接口获取鉴权dsn
* 实时从consul中获取服务信息
* 增加grpc recover中间件
## [v1.1.16] - 2022年3月4日15:11:00
### 修复
* globalctrl 协议转换, 修复错误字段origin->orgin
## [v1.1.15] - 2022年3月4日14:18:30
### 修复
* globalctrl 协议转换, turn_volume_to 分数 实体 转百分制数值
## [v1.1.10] - 时间
### 新增
* 接入grpc鉴权服务
### 修复
* 超时采用闲聊兜底
## [v1.0.10] - 2021年12月23日16:02:57
### 新增
* 增加"换一批歌曲"的解析
### 修复
* 修复无资源列表返回时不使用服务定义的激活提示回复语
## [v1.0.9] - 2021年11月26日15:27:50
### 新增
* 从配置文件读取测试音频的链接
## [v1.0.8] - 2021年11月11日15:21:34
### 修复
* 增加"声道测试"音频资源
## [v1.0.7] - 2021年11月8日16:23:53
### 修复
* 修复jokeDomain 资源列表为空的时造成的错误
## [v1.0.6] - 2021年10月27日10:03:47
### 修复
* 增加music next 的返回数据的合法检测, 当腾讯解析返回错误数据时进行兜底回复
## [v1.0.5] - 2021年10月19日14:03:45
### 修复
* 因腾讯问题返回jokeDomain资源列表为空时导致的数组越界问题
* alarmDomain 增加对ret字段的判断,不等于0是不进行闹钟的插入
### 新增
* mid 6400 及相应的appkey及token, 及相应的配置文件
```sql
INSERT INTO chatroom.mid_appkey
(idmid_appkey, mid, appkey, access_token, mode, uri_type, created_at, updated_at, deleted_at)
VALUES(101, '6400', 'd636a7e02d8111eca6607b3e5760da03', 'f4765fb437d342e2b4bdcf774db81882', 0, 0, '2021-10-15 15:56:45', NULL, NULL);
```
FROM golang:1.18 AS builder
WORKDIR /usr/src/tencent-nlu-parse
COPY ./go.mod /usr/src/tencent-nlu-parse/
COPY ./go.sum /usr/src/tencent-nlu-parse/
ENV GOPROXY=http://10.7.83.190:6666,https://proxy.golang.com.cn,direct CGO_ENABLED=0
RUN echo "hosts: files dns" > /etc/nsswitch.conf && \
echo -e "112.19.0.219 goproxy.cn\n157.255.30.117 mirrors.aliyun.com">>/etc/hosts && \
go mod download
COPY . /usr/src/tencent-nlu-parse/
RUN go build -o tencent-nlu-parse
FROM alpine as runner
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/src/tencent-nlu-parse/tencent-nlu-parse /tmp/
RUN echo -e "10.7.82.56 e-con-01\n10.7.83.97 m-nlp-03\n10.7.82.133 s-ipd-01\n10.7.80.153 c-gtw-01\n10.7.83.174 s-gtw-01">>/etc/hosts && \
ls /tmp/tencent-nlu-parse && \
chmod +x /tmp/tencent-nlu-parse
CMD ["/tmp/tencent-nlu-parse","-config=env/v2,tencent_nlu_parse/conf","-consul=http://e-con-01:8500", "-name=tencent_nlu_parse","-token=c89cb8e1-dc45-b8d2-1b06-c02684e09b21"]
#!/usr/bin/env groovy
pipeline{
agent any
options {
gitLabConnection('xiejunjie')
gitlabBuilds(builds: ['checkout', 'build','publish','deploy','release'])
// skipDefaultCheckout true
}
environment {
tagName = """${sh(returnStdout: true, script: 'git describe --abbrev=0 --tags').trim()}"""
shortCommit ="""${sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()}"""
registryDomain = ".gree.com"
repo = "docker_proxy"
port = 17971
jobName ="tencent-nlu-parse"
node="s-otr-01"
index="fluentd-tencent_nlu_parse"
}
triggers{
GenericTrigger(
genericVariables: [
[key: 'ref', value: '$.ref']
],
causeString: 'Triggered on $ref',
token: 'tencent-nlu-parse-b73b246d4bc9c5c9',
printContributedVariables: true,
printPostContent: true,
silentResponse: false,
regexpFilterText: '$ref',
//regexpFilterExpression: '^refs/heads/(dev\\d*|test|master)$'
)
}
stages{
stage("checkout"){
steps{
updateGitlabCommitStatus name: 'checkout', state: 'running'
checkout([
$class: 'GitSCM',
branches: [[
name: "refs/tags/$tagName"
]],
extensions: [[
$class: 'CloneOption',
noTags: false,
reference: '',
shallow: false
]],
userRemoteConfigs: [[
credentialsId: 'b56f8c12-cad2-41f0-a85f-36b3b3dc3241',
// credentialsId: '4ec77ef2-5f46-4b8a-b55c-f047f6fdae7d',
url: 'https://api.gree.com/gitlab/560310/tencent-nlu-parse.git'
]]
])
echo "Building shortCommit $tagName"
echo "Building ref $ref"
echo "get code from scm"
}
post {
failure {
updateGitlabCommitStatus name: 'checkout', state: 'failed'
}
aborted {
updateGitlabCommitStatus name: 'checkout', state: 'canceled'
}
success {
updateGitlabCommitStatus name: 'checkout', state: 'success'
}
}
}
stage("build"){
when {
anyOf{
environment name: 'deployEnv', value: 'testnlu'
environment name: 'deployEnv', value: 'nlu'
}
}
steps{
updateGitlabCommitStatus name: 'build', state: 'running'
sh """
docker build --add-host=goproxy.cn:112.54.108.85 --add-host=proxy.golang.com.cn:112.90.43.134 --add-host=mirrors.aliyun.com:182.89.221.213 --force-rm --no-cache --target runner -t $deployEnv$registryDomain/$repo/${jobName}:${tagName} -f Dockerfile .
"""
}
post {
failure {
updateGitlabCommitStatus name: 'build', state: 'failed'
}
aborted {
updateGitlabCommitStatus name: 'build', state: 'canceled'
}
success {
updateGitlabCommitStatus name: 'build', state: 'success'
}
}
}
stage("publish"){
when {
anyOf{
environment name: 'deployEnv', value: 'testnlu'
environment name: 'deployEnv', value: 'nlu'
}
}
steps{
updateGitlabCommitStatus name: 'publish', state: 'running'
withDockerRegistry([url: "https://testnlu.gree.com",credentialsId: "83d26e44-426f-494a-b771-aac60d07cd7c"]){
sh "docker push $deployEnv$registryDomain/$repo/${jobName}:${tagName}"
}
}
post {
failure {
updateGitlabCommitStatus name: 'publish', state: 'failed'
}
aborted {
updateGitlabCommitStatus name: 'publish', state: 'canceled'
}
success {
updateGitlabCommitStatus name: 'publish', state: 'success'
}
}
}
stage("deploy"){
steps{
echo "deployEnv $deployEnv"
updateGitlabCommitStatus name: 'deploy', state: 'running'
script{
withCredentials([usernamePassword(credentialsId: '83d26e44-426f-494a-b771-aac60d07cd7c', passwordVariable: 'passwd', usernameVariable: 'user')]) {
if (env.deployEnv=='testnlu'){
sshPublisher(
publishers: [sshPublisherDesc(configName: 'w-web-01',
transfers: [
sshTransfer(cleanRemote: false, excludes: '', execCommand: "if [ `sudo docker service ls |grep -w ${jobName}|wc -l` -ge 1 ];then sudo docker service rm ${jobName};fi && \
sudo docker login -u ${user} -p ${passwd} https://${deployEnv}${registryDomain} && \
sudo docker service create --with-registry-auth --name ${jobName} -p mode=host,published=${port},target=${port} --network test_gree --replicas 1 --constraint node.platform.os==linux --constraint node.hostname==${node} --log-driver=fluentd --log-opt fluentd-address=10.7.82.112:24224 --log-opt mode=non-blocking --log-opt tag=${index} --restart-condition on-failure --update-delay 10s --update-parallelism 1 --mount type=bind,source=/etc/hosts,target=/etc/hosts --mount type=bind,source=/lib64,target=/lib64 --mount type=bind,source=/var/log/service/${jobName},target=/var/log/service/${jobName} ${deployEnv}${registryDomain}/${repo}/${jobName}:${tagName}",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: ''
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)])
}else if (env.deployEnv=='nlu'){
sshPublisher(
publishers: [sshPublisherDesc(configName: 'a-gtw-01',
transfers: [
sshTransfer(cleanRemote: false, excludes: '', execCommand: "if [ `sudo docker ps |grep -w ${jobName}|wc -l` -ge 1 ];then sudo docker stop ${jobName};fi && \
if [ `sudo docker ps -af status=exited|grep -w ${jobName}|wc -l` -ge 1 ];then sudo docker rm `sudo docker ps -qf status=exited`;fi && \
sudo docker login -u ${user} -p ${passwd} https://${deployEnv}${registryDomain} && \
sudo docker run -d --name ${jobName} -p ${port}:${port} ${deployEnv}${registryDomain}/${repo}/${jobName}:${tagName}",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: ''
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)])
}
}
}
}
post {
failure {
updateGitlabCommitStatus name: 'deploy', state: 'failed'
}
aborted {
updateGitlabCommitStatus name: 'deploy', state: 'canceled'
}
success {
updateGitlabCommitStatus name: 'deploy', state: 'success'
}
}
}
stage("release") {
when {
anyOf{
environment name: 'deployEnv', value: 'testnlu'
environment name: 'deployEnv', value: 'nlu'
}
}
steps {
updateGitlabCommitStatus name: 'release', state: 'running'
echo "Building $BRANCH_NAME"
echo "shortCommit $shortCommit"
//echo "Building $TAG_NAME"
}
post {
failure {
updateGitlabCommitStatus name: 'release', state: 'failed'
}
aborted {
updateGitlabCommitStatus name: 'release', state: 'canceled'
}
success {
updateGitlabCommitStatus name: 'release', state: 'success'
}
}
}
}
}
### 控制服务
* 项目:tencent-nlu-parse
* 语言:Go
* 框架:gRPC
* 版本号:v1.1.44
* 版本控制:git-gitlab
* 用途:腾讯NLU协议转换服务
* 端口号:由运维同学进入consul中对应的自定义yaml文件中修改配置,自行定义
* 数据库: mysql:39服务器上的alarm_remind,test_content_url,mid_appkey表
* 更新日期: 2021.5.25
* author: 李立辉
* 更新内容:
1. 改为gRPC服务
---
#### 使用
* 请求
字段|类型|说明|默认值
:---:|:---:|:---:|:---:
macWifi|string|macWifi|无,必填
macVioce|string|macVoice|无,必填
query|string|query|无,必填
ip|string|ip|无,必填
testID|string|testID|无,必填
originQuery|string|originQuery|无,必填
mid|string|设备的mid, 用于获取botKey和botSecret.|无,必填
requestId|string|每次请求均不同且唯一,参考uuid. 为设备的请求服务时生成的requestId/uuid.|无,必填
lbs|-|经纬度信息|无,非必选
lbs.longitude|double(go对应float64)|经度|无,必选
lbs.latitude|double(go对应float64)|纬度|无,必选
* 响应
字段|类型|说明|默认值
:---:|:---:|:---:|:---:
status|object|响应状态|无,必选
status.code|int|0成功,非0失败|无,必选
status.msg|string|错误信息|无,必选
data|object|返回数据|无,必选
data.semanticRespJsonData|string|解析后腾讯nlu的json字符串|无,必选
* 启动服务
./test [-port=xxx] [-config=xxx] [-help] [-consul=xxx] [-token=xxx] [-debug=xxx]
说明:
1. -port int:启动端口,默认值在conf/config.yaml文件中设置;
2. -config string:配置文件位置,默认是consul中的env/v1.yaml文件,支持多个配置文件,使用“,”分隔。
3. -help: help
4. -consul string: consul地址, (default "http://172.28.5.39:8500")
5. -token string: consul token, (default "092288b5-824f-854c-39aa-a958afd9a633")
6. -debug bool: debug模式运行
注意:若需读取该服务自定义consul配置,需在config参数中填写包括env/v1.yaml文件和自定义文件!
* 查看服务信息
./test -version
* 服务接入
1. 将[proto文件](/pkg/proto/service.proto)的协议内容复制到所需接入的项目中的proto文件中进行编译, 一般放置在`Project/pkg`目录下. 执行以下指令进行编译`protoc -I=./ --go_out=plugins=grpc:. ./pkg/proto/service.proto`
2. client接入示例代码
```golang
func TencentNluParseExample(macWifi, macVoice, query, ip, originQuery, mid, requestId string) (string, error) {
// TODO: 修改grpc接入的ip:port
conn, err := grpc.Dial("172.28.5.44:8081", grpc.WithInsecure())
if err != nil {
return "", err
}
defer conn.Close()
c := proto.NewTencentNluClient(conn)
// TODO: 修改超时时间
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
// import ct context
// ctx, cancel := ct.WithTimeout(ct.Background(), time.Second*2)
defer cancel()
req := proto.SemanticRequest{MacWifi: macWifi, MacVoice: macVoice, Query: query, Ip: ip, OriginQuery: originQuery, Mid: mid, RequestId: requestId}
rep, err := c.TencentNluParse(ctx, &req)
if err != nil {
return "", err
}
// TODO: 增加对Status的判断
return rep.Data.SemanticRespJsonData, nil
}
func goForTencentNluGrpc(reqStruct *semantic.SemanticReq, resJsonStr chan string, exitSig chan bool) {
var channelStr string
// TODO: requestId 一次请求一个requestId不重复, 一般由设备进行请求
requestId := "test"
channelStr, err := TencentNluParseExample(reqStruct.MacWifi, reqStruct.MacVoice, reqStruct.Query, reqStruct.Ip, reqStruct.OriginQuery, reqStruct.Mid, requestId)
if err != nil {
return
}
select {
case <-exitSig:
return
case resJsonStr <- channelStr:
}
}
```
### 说明
- GetAuthorizationByGRPC 关于票据查询
code为200的时候切Appkey及accesstoken不为空时, 必然使用Appkey及accesstoken
有且仅当tokenSearchResponse.Data.Status为2是使用Appkey, accesstoken, auth, dsn
#### mid-appkey
* 测试服
|mid|appkey|
| ---- | ---- |
|11f03| 8d3b6076-3bb4-4c26-89c3-011447996044|
|11f00| 8d3b6076-3bb4-4c26-89c3-011447996044|
|10f04| ef9bc360baae11ea8cec1d1b4b4e3b98|
|10f05| 8d3b6076-3bb4-4c26-89c3-011447996044|
|11011| 8d3b6076-3bb4-4c26-89c3-011447996044|
|11009| 8d3b6076-3bb4-4c26-89c3-011447996044|
|10010| 8d3b6076-3bb4-4c26-89c3-011447996044|
|11008| 8d3b6076-3bb4-4c26-89c3-011447996044|
|10f10| 8d3b6076-3bb4-4c26-89c3-011447996044|
|11f02| 8d3b6076-3bb4-4c26-89c3-011447996044|
|10015_isdelete| 8d3b6076-3bb4-4c26-89c3-011447996044|
|10f03| 8d3b6076-3bb4-4c26-89c3-011447996044|
|10f00| 8d3b6076-3bb4-4c26-89c3-011447996044|
* mid_appkey 表字段说明
|key|type|info|
|----|----|----|
|mid|varchar(50)|设备型号|
|appkey|varchar(200)|云小微平台应用下的Appkey|
|access_token|varchar(200)|云小微平台应用下的AccessToken|
|app_secret|varchar(200)|云小微平台应用下的AppSecret|
|mode|int(11)|类型 0 qq音乐授权 1 酷狗音乐授权(已弃用)2厂商账号授权 3中控boot|
|uri_type|int(11)|类型 0 tencent_v1(默认) 1 腾讯_v2|
|vender|varchar(255)|细分码,默认为空字符串|
|need_activate|tinyint(1)|用于判断是否需要激活, 默认为1|
#### 错误码
| Code | Msg |
| ---- | ------------------ |
| 0 | 成功 |
| 1500 | 内部服务错误 |
| 1503 | 叮当服务超时 |
| 1504 | 叮当服务请求过快 |
Server:
RunMode: debug #release/debug/test
Name: speech_nlu_parse #注册到consul的服务名
Tag: #注意是字符数组形式,不允许空数据
IP: 172.28.124.110 #服务的IP
Port: 13302 #服务的端口
ConsulAddr: 172.28.124.105:8500 #consul的地址
Interval: 10 #健康检查间隔,单位:秒
Deregister: 1 #注销时间,相当于过期时间,单位:分钟
Logger:
LogFileName: tencent-nlu-parse
LogFileExt: .log
LogSavePath: /var/log/service/tencent-nlu-parse
MaxSize: 100 #在进行切割之前,日志文件的最大大小(MB为单位)
MaxAge: 30 #保留旧文件的最大天数
MaxBackups: 30 #保留旧文件的最大个数
Compress: false #是否压缩/归档旧文件
Device: #举例
Mids: #设备型号
- xxxxx
AppID: 4875316992302057035
AppKey: 8df39cf32e993d8325714e557c0dc865
DB:
Dsn: root:123456@tcp(172.28.124.105:3306)/chatroom?charset=utf8
MusicDomain:
IsQQMusicRemid: true # 是否替换为自定义的提醒
QQMusicRemid: 。您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。
AlbumList: [西厢记]
Auth:
AuthorizationTimeout: 1
# 关于酷狗的鉴权接口暂未上线正式服, 沿用旧版的鉴权服务
AuthorizationUrl: https://testnlu.gree.com/semantic/air/dm-sdk/auth
# AuthorizationUrl: http://172.28.5.30:7994/semantic/air/dm-sdk/auth
DingDang:
DingDangNluUrl: https://aiwx.html5.qq.com/api/v1/richanswerV2
# DingDangNluUrl: https://aiwx.html5.qq.com/apiexp/v1/richanswerV2 # 测试服 https://aiwx.sparta.html5.qq.com/api
TencentSha256Secret: secretformacwifi@2018zww
Others:
SoundTestUrl: https://yysbddzx.oss-cn-shenzhen.aliyuncs.com/audio-test/leftrighttest.mp3 # 声道测试音频
# 依赖的服务, 用于服务发现
Services:
Dmsdk: dm-sdk # 授权查询服务
DevLimited: # 设备差异化的配置, 通过mid查询对应的配置
-
Mids: ['10f03','test2'] # ap
Limited:
AlarmNum: -1 # 设置闹钟的数量限制, -1无限制
Recipe: false # 菜谱播报是否受限. 带屏的可以返回菜谱; 无屏进行提示. true:受限(不返回菜谱, 只进行提示), false:不受限(正常返回菜谱)
FmDomainAlbumList: ['西厢记'] # fm需要屏蔽的Album, 限制播放的内容
Memory: false # 内存是否受限, 无屏语音设备内存受限. true:受限, false:不受限
Screen: false # 屏幕是否受限, true:受限(不带屏), false:不受限(带屏)
HistoryNum: -1 # historyDomain 的条数, 仅在内存受限才生效, 例如"历史的今天" 只会播报五条
IsMusicRemid: true # 未授权的情况下是否进行授权提醒提醒, true:提醒, false:不提醒
MusicRemidText: 您的设备尚未授权,请前往格力+手机应用绑定授权。
# MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。 # 未授权的设备进行提醒, 替换到腾讯的回复语
ShieldedDomainList: [] # 需要进行屏蔽的
-
Mids: ['10f04'] # 儿童空调(B分体智慧鸟)
Limited:
AlarmNum: 15
Recipe: true
FmDomainAlbumList: ['西厢记']
Memory: true
Screen: true
HistoryNum: 5
IsMusicRemid: true # 测试服为true 暂不更新, 正式服为false; 正式服与测试服的也是有差异. 2022年2月14日11:27:43
MusicRemidText: 您的设备尚未授权,请前往格力+手机应用绑定授权。
# MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击酷狗音乐进行授权,授权过程不产生任何费用。
ShieldedDomainList: []
-
Mids: ['default'] # 其他空调(一般的语音空调), 默认(mid未出现在上面则使用这个设置)
Limited:
AlarmNum: 15
Recipe: true
FmDomainAlbumList: ['西厢记']
Memory: true
Screen: true
HistoryNum: 5
IsMusicRemid: true
MusicRemidText: 您的设备尚未授权,请前往格力+手机应用绑定授权。
# MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。
ShieldedDomainList: []
-
Mids: ['6400'] # 语音线控器
Limited:
AlarmNum: -1
Recipe: true
FmDomainAlbumList: ['西厢记']
Memory: false
Screen: true
HistoryNum: 5
# 该mid产品屏蔽了音乐领域, 该项不生效
IsMusicRemid: false
# 该mid产品屏蔽了音乐领域, 该项不生效
MusicRemidText: 您的设备尚未授权,请前往格力+手机应用绑定授权。
# MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。
ShieldedDomainList: ['music']
-
Mids: ['11f06'] # vp柜机
Limited:
AlarmNum: -1
Recipe: true
FmDomainAlbumList: ['西厢记']
Memory: false
Screen: false
HistoryNum: -1
IsMusicRemid: true
MusicRemidText: 您的设备尚未授权,请前往格力+手机应用绑定授权。
# MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。
ShieldedDomainList: ['alarm','reminder_v2']
-
Mids: ['10f05'] # 月亮女神 vp分体
Limited:
AlarmNum: -1
Recipe: false
FmDomainAlbumList: ['西厢记']
Memory: true
Screen: true
HistoryNum: -1
IsMusicRemid: true
MusicRemidText: 您的设备尚未授权,请前往格力+手机应用绑定授权。
# MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。
ShieldedDomainList: []
\ No newline at end of file
package dao
import (
"errors"
"speech-nlu-parse/model"
)
func GetTestContent(mac, query string) (err error, content, url string) {
selectSql := "select content, url from " + test_content_url + " where macwifi='" + mac + "' and query='" + query + "' limit 1"
rows, err := conn.Query(selectSql)
if err != nil {
return err, "", ""
}
defer rows.Close()
isEmpty := true
for rows.Next() {
isEmpty = false
err := rows.Scan(&content, &url)
if err != nil {
return err, "", ""
}
}
if isEmpty {
return errors.New("empty test content"), "", ""
}
return
}
// 查询是否全局自定义
func GetAnyTestContent(mac, query string) (err error, content, url string) {
selectSql := "select content, url from test_content_url where macwifi='any' and query='" + query + "'"
rows, err := conn.Query(selectSql)
if err != nil {
return err, "", ""
}
defer rows.Close()
isEmpty := true
for rows.Next() {
isEmpty = false
err := rows.Scan(&content, &url)
if err != nil {
return err, "", ""
}
}
if isEmpty {
return errors.New("empty test any content"), "", ""
}
return
}
func GetMidAppKey() ([]*model.MidAppKey, error) {
sql := "select mid, appkey, access_token, vender, uri_type from " + mid_appkey
rows, err := conn.Query(sql)
if err != nil {
return nil, err
}
defer rows.Close()
midAppKeyArr := make([]*model.MidAppKey, 0)
for rows.Next() {
var midAppKey = &model.MidAppKey{}
err = rows.Scan(&midAppKey.Mid, &midAppKey.AppKey, &midAppKey.AccessToken, &midAppKey.Vender, &midAppKey.UrlType)
if err != nil {
return nil, err
}
midAppKeyArr = append(midAppKeyArr, midAppKey)
}
return midAppKeyArr, nil
}
package dao
import (
"database/sql"
"fmt"
"time"
)
const (
//devDB = "de_records_ctocst" //空调聊天记录数据表
// devDB = "de_ctoclog_st"
//devDB = "de_records"
// asDB = "as_records" //语音助手聊条记录数据表
arDB = "alarm_remind" //闹钟数据表
// notsupportTB = "greenlu_notsupport" //格力NLU不支持的语句
// errorLog = "ctoc_errorlog"
// greenlu_semantic = "ctoc_greenlu_semantic"
// tencent_senmantic = "ctoc_tencent_semantic_log"
// control_semantic = "ctoc_control_semantic"
// skillUseDB = "ctoc_skill_user_like"
// homeinfoDB = "de_homeinfo_conn"
// ctoc_req_ala = "ctoc_req_ala"
test_content_url = "test_content_url"
mid_appkey = "mid_appkey"
)
const (
DevRecords = 1 << iota //空调聊天记录标志
AssRecords //语音助手聊条记录标志
)
const (
alarm = "0"
remind = "1"
countdown = "2"
)
var conn *sql.DB
func GetConn() *sql.DB {
return conn
}
func InitDB(dsn string) error {
var err error
conn, err = sql.Open("mysql", dsn)
if err != nil {
//log.Fatalln(err.Error() + " connect database error")
fmt.Println("ctocst[error]:connect database error!->" + err.Error())
//LogErrorToFile(logfileError, "database error:" + err.Error())
return err
}
err = conn.Ping()
if err != nil {
return err
}
conn.SetConnMaxLifetime(time.Minute * 3)
return nil
//conn.SetMaxOpenConns(1000)
//conn.SetMaxIdleConns(500)
}
package dao
import (
"database/sql"
"fmt"
"speech-nlu-parse/model"
"strconv"
"time"
)
// 保存闹钟、提醒数据
func insertAlarmRemind(alarmInfo model.AlarmRemindInfo, con *sql.DB) error {
errd := deleteOverDue(alarmInfo.Mac, con)
if errd != nil {
return errd
}
err, isExist := checkAlarmExist(alarmInfo, con)
if err != nil {
fmt.Println(err.Error() + "InsertAlarmRemind")
return err
} else {
if !isExist {
insertSql := "INSERT INTO " + arDB + "(mac,e_date,e_time,note,type,repeaet_type,status, " +
"countdown_duration, create_time, oid, url, content, speech_type) VALUES('" + alarmInfo.Mac + "'," +
"'" + alarmInfo.E_date + "','" + alarmInfo.E_time + "','" + alarmInfo.Note + "'," +
"'" + alarmInfo.E_type + "','" + strconv.Itoa(alarmInfo.Repeat_type) + "'," +
"'" + strconv.Itoa(alarmInfo.Status) + "','" + strconv.Itoa(alarmInfo.Countdown_duration) + "'," +
"'" + alarmInfo.Createtime + "','" + alarmInfo.Oid + "','" + alarmInfo.URL + "'," +
"'" + alarmInfo.Content + "'," +
"'" + alarmInfo.Speech_Type + "')"
_, err := con.Exec(insertSql)
if err != nil {
fmt.Println(err.Error() + insertSql)
}
return err
} else {
if alarmInfo.E_type == remind {
updateSql := "UPDATE " + arDB + " SET note='" + alarmInfo.Note + "', " +
"content='" + alarmInfo.Content + "' WHERE mac='" + alarmInfo.
Mac + "' AND e_date='" + alarmInfo.E_date + "' AND e_time='" + alarmInfo.E_time + "' AND repeaet_type='" + strconv.Itoa(alarmInfo.Repeat_type) + "' AND type='" + alarmInfo.E_type + "'"
_, err := con.Exec(updateSql)
if err != nil {
fmt.Println("update remind failed")
return err
}
}
fmt.Println(time.Now().String()[:19], alarmInfo.Oid, " exists")
}
}
return nil
}
// 删除过期闹钟
func deleteOverDue(mac string, con *sql.DB) error {
//select * from alarm_remind where repeaet_type = '1' AND (e_date <= date(now()) or (e_date <= date(now()) AND e_time <= time(now())))
//
deleteSql := "delete from " + arDB + " where mac='" + mac + "' AND repeaet_type = '1' AND (e_date < date(now()) or (e_date <= date(now()) AND e_time < time(now())))"
_, err := con.Exec(deleteSql)
if err != nil {
fmt.Println(err.Error())
return err
}
return nil
}
// 检查是否以及存在
func checkAlarmExist(alarmInfo model.AlarmRemindInfo, con *sql.DB) (error, bool) {
selectSql := "SELECT 1 FROM " + arDB + " where mac='" + alarmInfo.Mac + "' AND e_date= '" + alarmInfo.E_date + "' AND e_time='" + alarmInfo.E_time + "' AND type='" + alarmInfo.E_type + "' AND repeaet_type='" + strconv.Itoa(alarmInfo.Repeat_type) + "' limit 1"
var rows *sql.Rows
rows, err := con.Query(selectSql)
if err != nil {
fmt.Println(err.Error() + "->CheckAlarmExist")
}
defer rows.Close()
isExist := false
for rows.Next() {
isExist = true
}
return err, isExist
}
func deleteAlarm(alarmInfo model.AlarmRemindInfo, con *sql.DB) error {
errd := deleteOverDue(alarmInfo.Mac, con)
if errd != nil {
return errd
}
deleteSql := "DELETE FROM " + arDB + " WHERE mac='" + alarmInfo.Mac + "' AND oid= '" + alarmInfo.Oid + "'"
fmt.Println(deleteSql)
re, err := con.Exec(deleteSql)
if err != nil {
fmt.Println(err.Error() + " " + deleteSql)
return err
}
affectRows, err := re.RowsAffected()
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Printf("affected rows:%v", affectRows)
return nil
}
func deleteAllAlarm(mac string, con *sql.DB) error {
deleteSql := "DELETE FROM " + arDB + " WHERE mac='" + mac + "'"
// fmt.Println(deleteSql)
re, err := con.Exec(deleteSql)
if err != nil {
fmt.Println(err.Error() + " " + deleteSql)
return err
}
affectRows, err := re.RowsAffected()
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Printf("deleteAllAlarm affected rows:%v\n", affectRows)
return nil
}
// 保存闹钟提醒
func SaveAlarmRemindData(alarmData model.AlarmRemindInfo) {
err := insertAlarmRemind(alarmData, conn)
if err != nil {
fmt.Println(err.Error())
// utils.LogErrorToFile(*config.LogfileError, "database error(InsertAlarmRemind):"+err.Error())
}
}
// 取消闹钟
func DeleteAlarmRemindData(alarmData model.AlarmRemindInfo) {
err := deleteAlarm(alarmData, conn)
if err != nil {
fmt.Println(err.Error())
// utils.LogErrorToFile(*config.LogfileError, "database error(deleteAlarmRemindData):"+err.Error())
}
}
// 删除过期闹钟
func DeleteOverdueAlarmRemindData(mac string) {
err := deleteOverDue(mac, conn)
if err != nil {
fmt.Println(err.Error())
// utils.LogErrorToFile(*config.LogfileError, "database error(deleteOverdueAlarmRemindData):"+err.Error())
}
}
// 删除所有闹钟
func DeleteAllAlarmRemindData(mac string) {
err := deleteAllAlarm(mac, conn)
if err != nil {
fmt.Println(err.Error())
// utils.LogErrorToFile(*config.LogfileError, "database error(deleteOverdueAlarmRemindData):"+err.Error())
}
}
### play
字段|类型|说明|默认值
:---:|:---:|:---:|:---:
header|object|header||
header.semantic|object|semantic||
header.semantic.code|int|错误码||
header.semantic.domain|string|技能名称||
header.semantic.intent|string|意图||
header.semantic.msg|string|||
header.semantic.session_complete|bool|||
header.semantic.skill_id|string|||
response_text|string|回复语||
asr_recongize|string|输入话术||
listItems|arr|资源列表||
listItems[...].url|string|资源url||
listItems[...].singer|string|歌手||
listItems[...].song|string|歌名||
listItems[...].mediaId|string|资源id||
listItems[...].songId|string|音乐id||
原始json
```json
{
"header": {
"semantic": {
"code": 0,
"domain": "music",
"intent": "play",
"msg": "",
"session_complete": true,
"skill_id": "990835315751129088"
}
},
"response_text": "我挑了一些你可能会喜欢的歌,听听吧。",
"asr_recongize": "播放音乐",
"listItems": [{
"url": "http://isure6.stream.qqmusic.qq.com/C400003giDjB32HJqA.m4a?guid=2000000128&vkey=17AF5AAD48EE41F67C6F6ECD74BC5934E8E12DF3ACB131179BFADE4B6881BBD5C196E8C6F5D653CB44BDFC4458C16BDA16E3DC22A7ACD7B3&uin=&fromtag=184",
"singer": "dsslio",
"song": "黄碧云",
"mediaId": "234635158",
"songId": "234635158"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400000ZSjDB2nweDF.m4a?guid=2000000128&vkey=844781655A7CC1251C74818E2A4EC96CCCB4450B5557A08032AD3C9539E14D0BBFB33A5E4C475E258331889AC4582F91A6CC6142DC5CA494&uin=&fromtag=184",
"singer": "陈书宇ShuYu",
"song": "谁伸出援手",
"mediaId": "214004688",
"songId": "214004688"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400003GBheu2tDooN.m4a?guid=2000000128&vkey=8366F7355C57482F3B338D3543C8AB7D0CE4216DCA33650C4A62F3AABA0C0D989B69D882CAC9C9A67025905F3027576DA12050C0AB6D4008&uin=&fromtag=184",
"singer": "Setback Line",
"song": "29",
"mediaId": "233317760",
"songId": "233317760"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C4000041eN6V4ZM15Q.m4a?guid=2000000128&vkey=4F28F18137E3B53C6099075D1D8E5D110BE7148464AA05AD09A5FB979C5442C068AB2B8D08AF4F36A9B4111968A5B1E7EE9D219E8B282CF3&uin=&fromtag=184",
"singer": "好歌",
"song": "最后也没有",
"mediaId": "219220572",
"songId": "219220572"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C4000018VSdr3E89uK.m4a?guid=2000000128&vkey=525423C3178F462572772401674BD103D5E73BC1F40218AA922EB5834E6216DE1AA9B0B273470E48FAEC806B449BCB01006ADCBCF8583245&uin=&fromtag=184",
"singer": "小春",
"song": "苏州河边",
"mediaId": "236170226",
"songId": "236170226"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400001Ltf7646BNbb.m4a?guid=2000000128&vkey=41D071F8F8A11CB953BC552B8DAF033EEEDFE9DA0425DE288D9865EFC9AB7FE09B7A710329A00E4E4634A0C500E45283770C5ADCBF83ED6B&uin=&fromtag=184",
"singer": "圈儿",
"song": "旋律在心中",
"mediaId": "231086619",
"songId": "231086619"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400001oJ8Ac3h9dJW.m4a?guid=2000000128&vkey=E398AABFC01681D5C5D55FF1E7E0C5279CF18F9C0B28779789FC0023F128E149C576AD0C8E053182F2FC7D6C0165CF749E6F87D22F1D7D08&uin=&fromtag=184",
"singer": "紫薇",
"song": "云霞曲",
"mediaId": "212796064",
"songId": "212796064"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400002v68SZ2ulL5f.m4a?guid=2000000128&vkey=08797EA95ED28B72C0D9EA72A36E1B2E9DE0C2FE718E5F3A750BF9E162F506D655CF0AF14C6EA88C0669ED7D91B88CB427E2A280A5B0A9E9&uin=&fromtag=184",
"singer": "好歌",
"song": "不是快乐",
"mediaId": "219220805",
"songId": "219220805"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400004B6bhX323Tqa.m4a?guid=2000000128&vkey=2C771A0A2D524065E9D0AE3787E10AB83D618B029380BB5FAF0A9A0B6369F34CF1AFD6F3CDC277ED6273A09FEF82B137933E0C83756DDE23&uin=&fromtag=184",
"singer": "东缘阁",
"song": "洒脱",
"mediaId": "239478828",
"songId": "239478828"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400000JdxKl49vqUe.m4a?guid=2000000128&vkey=DF0F2B5BBA48F1B364631BB7003FDD9F11071E4BBFCB0D3DA236444577BE921DC20158BF02BD7BE70EB10437ECF4B4A72C71BA4A287C97BB&uin=&fromtag=184",
"singer": "全代扬",
"song": "长发",
"mediaId": "231270353",
"songId": "231270353"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400001cWLgO48eOCx.m4a?guid=2000000128&vkey=51070ABB7F7861664135D9DFDF6A1296409DD1FADC336DC363C8146B18DEE8353875407157DE94FA37E0FBAE11DC636F8050ADF684590DC0&uin=&fromtag=184",
"singer": "单沫文",
"song": "原谅我爱失落",
"mediaId": "232270316",
"songId": "232270316"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400004TcR2O3NEtOv.m4a?guid=2000000128&vkey=24A36E023350DF3FB9D1A0FD05E662FD238C988A9461B7DF452F90A8450D069B3B6E84842506CD11E48DC7DBEF21206E52A15D58267780A9&uin=&fromtag=184",
"singer": "圣殿骑士乐队",
"song": "翡翠号角",
"mediaId": "213767301",
"songId": "213767301"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400002ykGt01wbKzV.m4a?guid=2000000128&vkey=27663413B81AA926F172C7DAE9C8A367B0BBB773AC4145C41F503946523B325EDD041C5E402D48D9040F51CECC08F19EB99118EEA0C999DE&uin=&fromtag=184",
"singer": "田茜娜",
"song": "爱情宣告",
"mediaId": "231536655",
"songId": "231536655"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400002lol8s0AeGcb.m4a?guid=2000000128&vkey=002710C3915F5074EE3C5B27D10FEC9AF9699320F8977654335DE1419A6DAA28A86B1E40775F0C350A8C3B2ADEEB9CC600A2D3ABC6CC4012&uin=&fromtag=184",
"singer": "范书雅 Shuya",
"song": "李察吉尔",
"mediaId": "234435286",
"songId": "234435286"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400004bIQBJ4Gk4Dp.m4a?guid=2000000128&vkey=6B8784FB7EE323F66BC4886172C8608893A07A49EA6127CA4E17240FC1E5DD0E54229E229D1A60E4A1F30C9CA3FEB7B42B89B608B2DB3ECC&uin=&fromtag=184",
"singer": "戴阳",
"song": "到老好不好",
"mediaId": "101794547",
"songId": "101794547"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C40000367gQN3qmX87.m4a?guid=2000000128&vkey=1AF8F2F17F737D7C3F53A0B3F39FFAEE7BFC1BC0220E7DA36859A47E550AFABFC4FA36E7159923E8B102CC9E7ADB6C96590FAC070F74A117&uin=&fromtag=184",
"singer": "好歌",
"song": "又没有救",
"mediaId": "219220738",
"songId": "219220738"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400001MGRGL1jRUKn.m4a?guid=2000000128&vkey=ECA0D1729A4B6E20B8581354BF4EBBAEEFCB689B5BBF5866347CDA9A5C2BB076B39C425759810E46A0F2FF5CA8C85E341F39887B972B92AE&uin=&fromtag=184",
"singer": "P!SCO",
"song": "怎哭了",
"mediaId": "105540124",
"songId": "105540124"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C4000023KRcu16OSPK.m4a?guid=2000000128&vkey=1E172A71886CC8FF5D8E07DFDBD007C74A76EE9889F8704538DBED2393214681D465EB853D2EDB9F5AE438125A11CACD68EFC22DE1A08615&uin=&fromtag=184",
"singer": "人性先觉",
"song": "韧性",
"mediaId": "200871036",
"songId": "200871036"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400001pb5JS3sZGxu.m4a?guid=2000000128&vkey=C464413681724FE4D94AF67832C36DA491D680B9F6487D96171678B19FBDE48FBA1FE5D9F019C77A3FA71101C471B893F4B011F21F3E8D13&uin=&fromtag=184",
"singer": "LYRA",
"song": "如果",
"mediaId": "105532858",
"songId": "105532858"
}, {
"url": "http://isure6.stream.qqmusic.qq.com/C400004MHkkD43xKma.m4a?guid=2000000128&vkey=E3645FEF6EEAB5F983DCAB2700B92A3D09CDB5DFBF6EE00AD897E8A4FC35A44021A265B89D722EFA6B5006058BAD26372541C829C97385DF&uin=&fromtag=184",
"singer": "孙爱光/台北YMCA圣乐合唱团",
"song": "忆秦淮",
"mediaId": "202792220",
"songId": "202792220"
}]
}
```
\ No newline at end of file
package global
import (
"speech-nlu-parse/dao"
"speech-nlu-parse/model"
)
// 叮当 bot, key 和 secret, 服务启动从数据库查询并加载
// var DingDangBot map[string]*model.DingDangBot
var DingDangBot Bot
type Bot struct {
DingDangBot map[string]map[string]*model.DingDangBot // mid-midtype-bot
}
func NewBot() Bot {
return Bot{
DingDangBot: make(map[string]map[string]*model.DingDangBot),
}
}
func (this *Bot) Get(mid string, midType string) (*model.DingDangBot, bool) {
_, ok := this.DingDangBot[mid]
if !ok {
// mid不存在
return nil, false
}
bot, ok := this.DingDangBot[mid][midType]
if !ok {
// midType不存在
bot, ok = this.DingDangBot[mid][""]
if !ok {
// 默认的也不存在
Logger.Errorf("GetDingDangBot error.mid: %v, midType: %v", mid, midType)
return nil, false
}
return bot, true
} else {
return bot, true
}
}
func InitDingDangBot() error {
midAppKeyArr, err := dao.GetMidAppKey()
if err != nil {
return err
}
temp := NewBot()
for i := 0; i < len(midAppKeyArr); i++ {
if temp.DingDangBot[midAppKeyArr[i].Mid] == nil {
temp.DingDangBot[midAppKeyArr[i].Mid] = make(map[string]*model.DingDangBot)
}
temp.DingDangBot[midAppKeyArr[i].Mid][midAppKeyArr[i].Vender] = &model.DingDangBot{Key: midAppKeyArr[i].AppKey, Secret: midAppKeyArr[i].AccessToken, UrlType: midAppKeyArr[i].UrlType}
}
DingDangBot = temp
return nil
}
package global
const (
DOMAIN_PLAYCONTROL = "PlayControl"
INTENT_CONTROL_PAUSE = "control_pause"
INTENT_CONTROL_EXIT = "control_exit"
INTENT_CONTROL_STOP = "control_stop"
INTENT_CONTROL_NEXT = "control_next"
INTENT_CONTROL_RESUME = "control_resume"
INTENT_CONTROL_REPLAY = "control_replay"
INTENT_CONTROL_PREVIOUS = "control_previous"
INTENT_QUERY_SONG = "query_song"
)
package global
import (
"speech-nlu-parse/pkg/setting"
)
// 设备差异化
var DevLimitedArr []*setting.DevLimitedS
var limitedSetting map[string]*setting.LimitedS
// 临时写死
func InitDevLimitedArr() {
DevLimitedArr = append(DevLimitedArr, &setting.DevLimitedS{
Mids: []string{"default"},
Limited: setting.LimitedS{
AlarmNum: 15,
Recipe: true,
FmDomainAlbumList: []string{"西厢记"},
Memory: true,
Screen: true,
HistoryNum: 5,
IsMusicRemid: true,
MusicRemidText: "您的设备尚未授权,请前往格力+手机应用绑定授权。",
ShieldedDomainList: []string{},
},
})
DevLimitedArr = append(DevLimitedArr, &setting.DevLimitedS{
Mids: []string{"10f03"},
Limited: setting.LimitedS{
AlarmNum: -1, // 设置闹钟的数量限制, -1无限制
Recipe: false, // 菜谱播报是否受限. 带屏的可以返回菜谱; 无屏进行提示. true:受限(不返回菜谱, 只进行提示), false:不受限(正常返回菜谱)
FmDomainAlbumList: []string{"西厢记"}, // fm需要屏蔽的Album, 限制播放的内容
Memory: false, // 内存是否受限, 无屏语音设备内存受限. true:受限, false:不受限
Screen: false, // 屏幕是否受限, true:受限(不带屏), false:不受限(带屏)
HistoryNum: -1, // historyDomain 的条数, 仅在内存受限才生效, 例如"历史的今天" 只会播报五条
IsMusicRemid: true, // 未授权的情况下是否进行授权提醒提醒, true:提醒, false:不提醒
MusicRemidText: "您的设备尚未授权,请前往格力+手机应用绑定授权。", // MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击QQ音乐进行授权,授权过程不产生任何费用。 # 未授权的设备进行提醒, 替换到腾讯的回复语
ShieldedDomainList: []string{}, // 需要进行屏蔽的
},
})
DevLimitedArr = append(DevLimitedArr, &setting.DevLimitedS{
Mids: []string{"10f04"},
Limited: setting.LimitedS{
AlarmNum: 15,
Recipe: true,
FmDomainAlbumList: []string{"西厢记"},
Memory: true,
Screen: true,
HistoryNum: 5,
IsMusicRemid: false, // 测试服为true, 正式服为false; 正式服与测试服的也是有差异, 原因: 未切换酷狗音乐. 2022年2月14日11:27:43
MusicRemidText: "您的设备尚未授权,请前往格力+手机应用绑定授权。", // MusicRemidText: 您的语音空调尚未激活,请尽快更新格力+APP,进入语音空调语音技能页面,点击酷狗音乐进行授权,授权过程不产生任何费用。
ShieldedDomainList: []string{},
},
})
DevLimitedArr = append(DevLimitedArr, &setting.DevLimitedS{
Mids: []string{"6400"},
Limited: setting.LimitedS{
AlarmNum: -1,
Recipe: true,
FmDomainAlbumList: []string{"西厢记"},
Memory: false,
Screen: true,
HistoryNum: 5,
IsMusicRemid: false, // 该mid产品屏蔽了音乐领域, 该项不生效
MusicRemidText: "您的设备尚未授权,请前往格力+手机应用绑定授权。", // 该mid产品屏蔽了音乐领域, 该项不生效
ShieldedDomainList: []string{"music"},
},
})
DevLimitedArr = append(DevLimitedArr, &setting.DevLimitedS{
Mids: []string{"11f06"},
Limited: setting.LimitedS{
AlarmNum: -1,
Recipe: false,
FmDomainAlbumList: []string{"西厢记"},
Memory: false,
Screen: false,
HistoryNum: -1,
IsMusicRemid: false,
MusicRemidText: "您的设备尚未授权,请前往格力+手机应用绑定授权。",
ShieldedDomainList: []string{},
},
})
DevLimitedArr = append(DevLimitedArr, &setting.DevLimitedS{
Mids: []string{"10f05"},
Limited: setting.LimitedS{
AlarmNum: -1,
Recipe: false,
FmDomainAlbumList: []string{"西厢记"},
Memory: true,
Screen: true,
HistoryNum: -1,
IsMusicRemid: false,
MusicRemidText: "您的设备尚未授权,请前往格力+手机应用绑定授权。",
ShieldedDomainList: []string{},
},
})
}
func InitLimitedSetting() {
limitedSetting = make(map[string]*setting.LimitedS)
// 临时写死
// 删除写死操作
// InitDevLimitedArr()
// 初始化
for i := 0; i < len(DevLimitedArr); i++ {
for j := 0; j < len(DevLimitedArr[i].Mids); j++ {
limitedSetting[DevLimitedArr[i].Mids[j]] = &DevLimitedArr[i].Limited
}
}
}
// 通过mid获取配置
func GetLimitedSetting(mid string) *setting.LimitedS {
value, ok := limitedSetting[mid]
if ok {
return value
} else {
return limitedSetting["default"]
}
}
package global
import (
"math/rand"
"speech-nlu-parse/model"
"speech-nlu-parse/pkg/consul"
"github.com/hashicorp/consul/api"
)
// 从consul中获取
var serviceMap map[string][]*model.Service
var (
consulUrl string
consulToken string
)
var ConsulCient *api.Client
func InitConsulCient(url, token string) {
config := api.DefaultConfig()
config.Address = url
config.Token = token
var err error
ConsulCient, err = api.NewClient(config)
if err != nil {
panic(err)
}
}
func InitServiceMap(url string, token string) {
consulUrl = url
consulToken = token
InitConsulCient(consulUrl, consulToken)
}
func GetService(name string) *model.Service {
// 重新从consul上获取
services := consul.GetService2(ConsulCient, name, "")
if services == nil || len(services) <= 0 {
return nil
} else {
index := rand.Intn(len(services))
return &model.Service{Address: services[index].Service.Address, Port: services[index].Service.Port}
}
}
package global
import (
"speech-nlu-parse/pkg/consul"
"speech-nlu-parse/pkg/logger"
"speech-nlu-parse/pkg/setting"
)
var (
ServerSetting *consul.ConsulSettingS
LoggerSetting *setting.LogSettingS
DeviceSetting *setting.DeviceSettingS
Logger *logger.Logger
InternalRemoteSetting *setting.InternalRemoteSettingS
DBSetting *setting.DBSettingS
MusicDomainSetting *setting.MusicDomainSettingS
AuthSetting *setting.AuthSettingS
TencentSetting *setting.TencentSettingS
OthersSetting *setting.OthersSettingS
ServiceSetting *setting.ServiceSettingS
ConsulObj *consul.ConsulObj
TencentGwSetting *setting.TencentGwSetting
)
module speech-nlu-parse
go 1.18
require (
github.com/go-sql-driver/mysql v1.7.1
github.com/golang/protobuf v1.5.3
github.com/gorilla/websocket v1.5.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/hashicorp/consul/api v1.21.0
github.com/jinzhu/gorm v1.9.16
github.com/json-iterator/go v1.1.12
github.com/parnurzeal/gorequest v0.2.16
github.com/spf13/viper v1.16.0
github.com/tidwall/gjson v1.14.4
go.uber.org/zap v1.24.0
google.golang.org/grpc v1.56.0
google.golang.org/protobuf v1.30.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require (
github.com/armon/go-metrics v0.4.0 // indirect
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/smartystreets/goconvey v1.8.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
moul.io/http2curl v1.0.0 // indirect
)
This diff is collapsed.
package main
import (
"errors"
"flag"
"fmt"
"log"
"net"
"net/url"
"runtime"
"strings"
"time"
"speech-nlu-parse/dao"
"speech-nlu-parse/global"
"speech-nlu-parse/middleware"
"speech-nlu-parse/pkg/consul"
"speech-nlu-parse/pkg/logger"
"speech-nlu-parse/pkg/proto"
"speech-nlu-parse/pkg/setting"
"speech-nlu-parse/service"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/reflection"
"gopkg.in/natefinch/lumberjack.v2"
)
var (
serverName string
serverTag string
ip string
port int
consulAddr string
config string
runMode string
isHelp bool
isVersion bool
buildTime string
buildVersion string
author string
consulToken string
consulUrl string
)
func setup() error {
// 命令行读取参数
err := setupFlag()
if err != nil {
log.Fatalf("init.setupFlag err: %v", err)
return err
}
// 判断help
if isHelp {
fmt.Println("编译后\n" +
"Linux可执行: ./tencent-nlu-parse [command=xxx]\n" +
"Windows可执行: ./tencent-nlu-parse.exe [command=xxx]\n\n" +
"option:")
flag.PrintDefaults()
return errors.New("missing params.")
}
if isVersion {
fmt.Println("build_time: ", buildTime)
fmt.Println("build_version: ", buildVersion)
fmt.Println("go_version: ", runtime.Version())
fmt.Println("author: ", author)
return errors.New("missing params.")
}
err = setupSetting()
if err != nil {
log.Fatalf("init.setupSetting err: %v", err)
return err
}
err = setupLogger()
if err != nil {
log.Fatalf("init.setupLogger err: %v", err)
return err
}
return nil
}
func main() {
var err error
// 初始化, 加载配置
err = setup()
if err != nil {
return
}
lis, err := net.Listen("tcp", fmt.Sprintf(":%v", global.ServerSetting.Port))
if err != nil {
log.Fatalf("listen error:%v", err)
}
server := grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
middleware.StreamGSError500(),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
middleware.UnaryGSError500(),
)),
)
proto.RegisterTencentNluServer(server, &service.TencentNlu{})
_ = consul.RegisterService(global.ServerSetting) //将服务注册到注册中心
grpc_health_v1.RegisterHealthServer(server, &consul.HealthImpl{}) //执行健康检查
reflection.Register(server) //使用grpcurl、grpcui工具需添加该行
global.Logger.Info("service is running......")
if err = server.Serve(lis); err != nil {
log.Fatalf("start service error:%v", err)
}
}
func setupFlag() error {
flag.BoolVar(&isHelp, "help", false, "帮助")
flag.StringVar(&serverName, "name", "", "注册到注册中心的服务名")
flag.StringVar(&serverTag, "tag", "", "注册到注册中心的标签")
flag.StringVar(&ip, "ip", "", "IP")
flag.IntVar(&port, "port", 0, "端口")
flag.StringVar(&consulUrl, "consul", "http://172.28.124.105:8500", "consul服务地址")
flag.StringVar(&config, "config", "env/v2,speech_nlu_parse/conf", "指定要使用的配置文件路径")
flag.BoolVar(&isVersion, "version", false, "编译信息")
flag.StringVar(&consulToken, "token", "092288b5-824f-854c-39aa-a958afd9a633", "consul token")
flag.StringVar(&runMode, "mode", "", "启动模式(debug|release|test)")
flag.Parse()
return nil
}
func setupSetting() error {
// 设置 consul 地址 token及路径
s, err := setting.NewSetting(consulUrl, consulToken, strings.Split(config, ",")...)
if err != nil {
return err
}
err = s.ReadSection("Server", &global.ServerSetting)
if err != nil {
return err
}
err = s.ReadSection("Logger", &global.LoggerSetting)
if err != nil {
return err
}
err = s.ReadSection("Device", &global.DeviceSetting)
if err != nil {
return err
}
err = s.ReadSection("gree", &global.InternalRemoteSetting)
if err != nil {
return err
}
err = s.ReadSection("DB", &global.DBSetting)
if err != nil {
return err
}
err = s.ReadSection("MusicDomain", &global.MusicDomainSetting)
if err != nil {
return err
}
err = s.ReadSection("Auth", &global.AuthSetting)
if err != nil {
return err
}
err = s.ReadSection("DingDang", &global.TencentSetting)
if err != nil {
return err
}
err = s.ReadSection("Others", &global.OthersSetting)
if err != nil {
return err
}
err = s.ReadSection("Services", &global.ServiceSetting)
if err != nil {
return err
}
// 改由配置文件读取
err = s.ReadSection("DevLimited", &global.DevLimitedArr)
if err != nil {
return err
}
err = s.ReadSection("tencentV2", &global.TencentGwSetting)
if err != nil {
return err
}
consulUrlParse, err := url.Parse(consulUrl)
if err != nil {
return err
}
global.ServerSetting.ConsulAddr = consulUrlParse.Host
// 初始化依赖服务的信息
global.InitServiceMap(consulUrl, consulToken)
// global.CheckServiceMap()
// global.PrintServiceMap()
// 初始化设备差异化配置, 需要先加载设置设置
global.InitLimitedSetting()
// 初始化数据库连接, 依赖于设置
err = dao.InitDB(global.DBSetting.Dsn)
if err != nil {
return err
}
// 从数据库中加载, 依赖于数据库连接
err = global.InitDingDangBot()
if err != nil {
return err
}
global.ServerSetting.Interval *= time.Second
global.ServerSetting.Deregister *= time.Minute
if serverName != "" {
global.ServerSetting.Name = serverName
}
if serverTag != "" {
global.ServerSetting.Tag = strings.Split(serverTag, ",")
}
if ip != "" {
global.ServerSetting.IP = ip
}
if port != 0 {
global.ServerSetting.Port = port
}
if consulToken != "" {
global.ServerSetting.ConsulToken = consulToken
}
if consulToken != "" {
global.ServerSetting.ConsulToken = consulToken
}
if runMode != "" {
global.ServerSetting.RunMode = runMode
}
global.ConsulObj, err = consul.NewConsulObj(consulUrl, consulToken)
if err != nil {
return err
}
return nil
}
func setupLogger() error {
fileName := global.LoggerSetting.LogSavePath + "/" +
global.LoggerSetting.LogFileName + "_" + time.Now().Format("20060102150405") + global.LoggerSetting.LogFileExt
global.Logger = logger.NewLogger(&lumberjack.Logger{
Filename: fileName,
MaxSize: global.LoggerSetting.MaxSize,
MaxAge: global.LoggerSetting.MaxAge,
MaxBackups: global.LoggerSetting.MaxBackups,
Compress: global.LoggerSetting.Compress,
}, global.ServerSetting.RunMode, global.ServerSetting.Name)
return nil
}
package middleware
import (
"context"
"runtime/debug"
"speech-nlu-parse/global"
"speech-nlu-parse/pkg/logger"
"google.golang.org/grpc"
)
// StreamGSError500 捕捉流式代码致命错误
func StreamGSError500() grpc.StreamServerInterceptor {
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
defer func() {
if err := recover(); err != nil {
//打印错误堆栈信息
// global.Logger.WithFields(logger.Fields{}).Panicf("panic: %v", err)
global.Logger.Panicf("panic: %v, stack: %v", err, string(debug.Stack()))
}
}()
return handler(srv, stream)
}
}
// UnaryGSError500 捕捉简单代码致命错误
func UnaryGSError500() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
defer func() {
if err := recover(); err != nil {
//打印错误堆栈信息
global.Logger.WithFields(logger.Fields{"request": req}).Panicf("panic: %v, stack: %v", err, string(debug.Stack()))
}
}()
return handler(ctx, req)
}
}
package model
type Lbs struct {
Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"`
}
type AddressInfo struct {
Province string `json:"province"`
City string `json:"city"`
County string `json:"county"`
}
type SemanticReqNativeData struct {
// TODO: Native API协议数据, 由云端和客户端一起定义
}
type SemanticReqNative struct {
Name string `json:"name"`
SeqID string `json:"seqID"`
Code int `json:"code"`
Message string `json:"message"`
DataVersion string `json:"dataVersion"`
Data *SemanticReqNativeData `json:"data"`
}
type SemanticReqSDKExtend struct {
CarDOA int `json:"carDOA"`
LastRspType string `json:"lastRspType"`
LastRspResult string `json:"lastRspResult"`
LastSessionStatus int `json:"lastSessionStatus"`
LastCompleteCmdResult string `json:"lastCompleteCmdResult"`
}
// SemanticReq store data come from client request json.
type SemanticReq struct {
// CheckStr string `json:"checkstr,omitempty"`
// ServiceID string `json:"serviceId,omitempty"`
// SessionID string `json:"sessionId,omitempty"`
// AppId string `json:"appId,omitempty"`
// //Token string `json:"token,omitempty"`
// Mac string `json:"mac,omitempty"`
MacWifi string `json:"macWifi,omitempty"`
MacVoice string `json:"macVoice,omitempty"`
// Barcode string `json:"barcode,omitempty"`
Query string `json:"query,omitempty"`
Ip string `json:"ip,omitempty"`
// TimeStamp string `json:"timestamp,omitempty"`
// Classify string `json:"classify,omitempty"`
TestID string `json:"testId,omitempty"`
// //UserID string `json:"uid,omitempty"`
// //VerServer string `json:"ver,omitempty"`
// HomeId string `json:"homeId,omitempty"`
// City string `json:"city,omitempty"`
// BotKey string `json:"botKey,omitempty"`
// Appver string `json:"appVer,omitempty"`
// RequestData string `json:"requestBody,omitempty"`
OriginQuery string `json:"oriQuery,omitempty"`
// Mode string `json:"mode"` //是否儿童模式
Mid string `json:"mid,omitempty"`
// Version string `json:"version,omitempty"`
RequestId string
Lbs *Lbs `json:"lbs,omitempty"` // 经纬度
Vender string `json:"vender,omitempty"` // 细分码
Appkey string `json:"appKey"`
AccessToken string `json:"accessToken"`
Qua string `json:"qua"`
Auth string `json:"auth"`
Dsn string `json:"dsn"`
Guid string `json:"guid"`
Exist bool `json:"exist"`
TRequestId string `json:"tRequestId"`
RequestType string `json:"requestType"`
CommType string `json:"commType"`
CharacterID string `json:"characterID"`
Event string `json:"event"`
Nativate *SemanticReqNative `json:"nativate"`
SDKExtend *SemanticReqSDKExtend `json:"sdkExtend"`
AddressInfo *AddressInfo `json:"addressInfo"`
Language string `json:"language"`
}
// Tencent report data
type TencentReport struct {
UserId string
Domain string
Intent string
ResourceId string
DataSource string
}
type RicAnswer struct {
Header struct {
Guid string `json:"guid,omitempty"`
Device struct {
SerialNum string `json:"serial_num,omitempty"`
} `json:"device,omitempty"`
User struct {
Auth string `json:"authorization,omitempty"`
} `json:"user"`
Qua string `json:"qua"`
Ip string `json:"ip,omitempty"`
Lbs *Lbs `json:"lbs,omitempty"`
} `json:"header,omitempty"`
Payload struct {
Query string `json:"query"`
} `json:"payload"`
}
type AlarmRemindInfo struct {
Mac string
E_date string
E_time string
Note string
E_type string
Repeat_type int
Start_time int
Status int
Last_update_time int
Countdown_duration int
Createtime string
Oid string
URL string
Content string
Speech_Type string
}
// domain 方法的参数
type DomainParams struct {
JsonData []byte
Mac string
Query string
Domain string
Mid string
TokenSearchResponse *TokenSearchResponse
}
// domain 方法返回的结果
type DomainRes struct {
JsonByte []byte
ReportDatas []TencentReport
}
type DingDangBot struct {
Key string
Secret string
UrlType int // url类型, 用于新旧设备的区分
}
type MidAppKey struct {
Mid string
AppKey string
AccessToken string
Vender string // 细分码
UrlType int // url类型, 用于新旧设备的区分
}
type GreeNluProtocol struct {
Status struct {
Code int `json:"code"`
ErrorType string `json:"errorType"`
} `json:"status"`
Query string `json:"query"`
Semantic *map[string]interface{} `json:"semantic"`
Result struct {
Hint string `json:"hint"`
ResultData *map[string]interface{} `json:"resultData"`
} `json:"result"`
}
type ParamsStr struct {
Origin string `json:"orgin"`
Norm string `json:"norm"`
Code int `json:"code"`
}
type OutputContextStr struct {
Context string `json:"context"`
Service string `json:"service"`
Class string `json:"class"`
Scene string `json:"scene"`
}
type TokenSearchRequest struct {
Mac string `json:"mac"`
RequestId string `json:"requestId"`
}
type TokenSearchResponse struct {
Status struct {
Code int32 `json:"code"`
Msg string `json:"msg"`
} `json:"status"`
Data struct {
Dsn string `json:"dsn"`
Authorization string `json:"authorization"`
AccessToken string `json:"accessToken"`
AppKey string `json:"appKey"`
Status int32 `json:"status"`
UriType int32 `json:"uriType"`
HomeId string `json:"homeId"`
} `json:"data"`
}
// 服务
type Service struct {
Address string
Port int
}
type Location struct {
Longitude float32 `json:"Longitude"`
Latitude float32 `json:"Latitude"`
City string `json:"City,omitempty"`
}
type TencentNlp struct {
Header struct {
QUA string `json:"QUA"`
DSN string `json:"DSN"`
GUID string `json:"GUID,omitempty"`
Location *Location `json:"Location,omitempty"`
} `json:"Header"`
Payload struct {
Semantic struct {
Query string `json:"Query"`
} `json:"Semantic"`
RequestType string `json:"RequestType,omitempty"`
CommType string `json:"CommType,omitempty"`
} `json:"Payload"`
}
type DomainParamsV2 struct {
TencentNlpResp *TencentNlpResp
Mac string
Query string
Domain string
Mid string
MidType string
TokenSearchResponse *TokenSearchResponse
RequestId string
}
type PayloadDataNative struct {
Name string `json:"Name"`
SeqID string `json:"SeqID"`
Timeout int `json:"Timeout"`
Param map[string]string `json:"Param"`
}
type PayloadData struct {
Class string `json:"Class"`
Native PayloadDataNative `json:"Native"`
}
type ResponseBody struct {
Header struct {
Semantic struct {
Code int `json:"code"`
Domain string `json:"domain"`
Intent string `json:"intent"`
Msg string `json:"msg"`
SessionComplete bool `json:"session_complete"`
SkillId string `json:"skill_id"`
Slots []map[string]interface{} `json:"slots,omitempty"`
Command interface{} `json:"Command,omitempty"`
ResponseType string `json:"ResponseType,omitempty"`
Data *PayloadData `json:"Data,omitempty"`
} `json:"semantic"`
} `json:"header"`
ResponseText string `json:"response_text"`
AsrRecongize string `json:"asr_recongize"`
ListItems []map[string]interface{} `json:"listItems"`
}
type SDKExtend struct {
CarDOA int `json:"CarDOA"`
LastRspType string `json:"LastRspType"`
LastRspResult string `json:"LastRspResult"`
LastSessionStatus int `json:"LastSessionStatus"`
LastCompleteCmdResult string `json:"LastCompleteCmdResult"`
}
type Native struct {
Name string `json:"Name"`
SeqID string `json:"SeqID"`
Code int `json:"Code"`
Message string `json:"Message"`
DataVersion string `json:"DataVersion"`
Data struct {
DataArr []map[string]string `json:"List"`
} `json:"Data"`
}
// ws req 协议
type TencentNlpWs struct {
Header struct {
RequestID string `json:"RequestID,omitempty"`
DialogID string `json:"DialogID,omitempty"`
GUID string `json:"GUID,omitempty"`
QUA string `json:"QUA,omitempty"`
AppKey string `json:"AppKey,omitempty"`
DSN string `json:"DSN,omitempty"`
Location *Location `json:"Location,omitempty"`
} `json:"Header"`
Payload struct {
RequestType string `json:"RequestType,omitempty"`
CommType string `json:"CommType,omitempty"`
Common struct {
GUID string `json:"GUID,omitempty"`
QUA string `json:"QUA,omitempty"`
AppeKey string `json:"AppeKey,omitempty"`
DSN string `json:"DSN,omitempty"`
CharacterID string `json:"CharacterID,omitempty"`
} `json:"Common"`
SDKExtend *SDKExtend `json:"SDKExtend,omitempty"`
Semantic struct {
Query string `json:"Query,omitempty"`
Event string `json:"Event,omitempty"`
Language string `json:"Language,omitempty"`
} `json:"Semantic,omitempty"`
Native *Native `json:"Native,omitempty"`
} `json:"Payload"`
}
type DomainParamsWs struct {
TencentNlpResp *TencentNlpWsResp
Query string
RequestId string
Mac string
Domain string
Mid string
MidType string
TokenSearchResponse *TokenSearchResponse
SessionId string
TencentNlpWs interface{}
HomeId string
AppKey string
}
type TencentNluParseStreamMetaData struct {
MacWifi string `json:"macWifi,omitempty"`
MacVoice string `json:"macVoice,omitempty"`
Ip string `json:"ip,omitempty"`
Mid string `json:"mid,omitempty"`
RequestId string
Vender string `json:"vender,omitempty"` // 细分码
Appkey string `json:"appKey"`
AccessToken string `json:"accessToken"`
Qua string `json:"qua"`
Auth string `json:"auth"`
Dsn string `json:"dsn"`
Exist bool `json:"exist"`
TRequestId string `json:"tRequestId"`
SessionId string `json:"sessionId"`
HomeId string `json:"homeId"`
}
package model
type SpeechDomainParams struct {
SpeechWsResp *SpeechWsResp
Query string
RequestId string
Mac string
Domain string
Mid string
MidType string
SessionId string
HomeId string
AppKey string
}
type SpeechWsResp struct {
RequestId string `json:"requestId"`
Dm *struct {
Param *struct {
Period string `json:"period"`
Object string `json:"object"`
Time string `json:"time"`
Date string `json:"date"`
Operation string `json:"operation"`
Event string `json:"event"`
Repeat string `json:"repeat"`
Operate string `json:"operate"`
Number string `json:"number"`
} `json:"param"`
IntentName string `json:"intentName"`
Input string `json:"input"`
IntentId string `json:"intentId"`
RunSequence string `json:"runSequence"`
Widget *struct {
WidgetName string `json:"widgetName"`
SubTitle string `json:"subTitle"`
Name string `json:"name"`
Extra struct {
ContentTranslation string `json:"content_translation"`
Title string `json:"title"`
} `json:"extra"`
Title string `json:"title"`
Buttons []struct {
Name string `json:"name"`
} `json:"buttons"`
DuiWidget string `json:"duiWidget"`
Type string `json:"type"`
Count int `json:"count"`
CurrentPage int `json:"currentPage"`
TotalPages int `json:"totalPages"`
ItemsPerPage int `json:"itemsPerPage"`
Match int `json:"match"`
Content interface{} `json:"content"`
//Content []struct {
// LinkUrl string `json:"linkUrl"`
// ImageUrl string `json:"imageUrl"`
// SubTitle string `json:"subTitle"`
// Album string `json:"album"`
// Parameters struct {
// ResType string `json:"resType"`
// } `json:"parameters"`
// Title string `json:"title"`
// Author string `json:"author"`
// Text string `json:"text"`
// Extra struct {
// Dynasty string `json:"dynasty"`
// ResType string `json:"resType"`
// } `json:"extra"`
//} `json:"content"`
} `json:"widget"`
Nlg string `json:"nlg"`
Task string `json:"task"`
ShouldEndSession bool `json:"shouldEndSession"`
Api string `json:"api"`
Command *struct {
Param *struct {
Volume string `json:"volume"`
Mode string `json:"mode"`
} `json:"param"`
Api string `json:"api"`
} `json:"command"`
} `json:"dm"`
SpeakUrl string `json:"speakUrl"`
SkillId string `json:"skillId"`
Skill string `json:"skill"`
ContextId string `json:"contextId"`
Error *struct {
ErrId interface{} `json:"errId"`
ErrMsg string `json:"errMsg"`
} `json:"error"`
RecordId string `json:"recordId"`
SessionId string `json:"sessionId"`
}
type Content struct {
LinkUrl string `json:"linkUrl"`
ImageUrl string `json:"imageUrl"`
SubTitle string `json:"subTitle"`
Album string `json:"album"`
Parameters struct {
ResType string `json:"resType"`
} `json:"parameters"`
Title string `json:"title"`
Author string `json:"author"`
Text string `json:"text"`
Extra struct {
Dynasty string `json:"dynasty"`
ResType string `json:"resType"`
} `json:"extra"`
}
package model
func (d *SpeechDomainParams) CheckDm() bool {
if d.SpeechWsResp.Dm == nil {
return false
}
return true
}
func (d *SpeechDomainParams) CheckWidget() bool {
if !d.CheckDm() || d.SpeechWsResp.Dm.Widget == nil {
return false
}
return true
}
func (d *SpeechDomainParams) CheckDmParam() bool {
if !d.CheckDm() || d.SpeechWsResp.Dm.Param == nil {
return false
}
return true
}
func (d *SpeechDomainParams) CheckEvent() bool {
if !d.CheckDmParam() || d.SpeechWsResp.Dm.Param.Event == "" {
return false
}
return true
}
func (d *SpeechDomainParams) CheckDate() bool {
if !d.CheckDmParam() || d.SpeechWsResp.Dm.Param.Date == "" {
return false
}
return true
}
func (d *SpeechDomainParams) CheckRepeat() bool {
if !d.CheckDmParam() || d.SpeechWsResp.Dm.Param.Repeat == "" {
return false
}
return true
}
func (d *SpeechDomainParams) CheckDmCommand() bool {
if !d.CheckDm() || d.SpeechWsResp.Dm.Command == nil {
return false
}
return true
}
func (d *SpeechDomainParams) CheckCommandParam() bool {
if !d.CheckDmCommand() || d.SpeechWsResp.Dm.Command.Param == nil {
return false
}
return true
}
package model
type ListItem struct {
Title string `json:"title"`
TextContent string `json:"textContent"`
Audio struct {
Stream struct {
Url string `json:"url"`
} `json:"stream"`
} `json:"audio"`
MediaId string `json:"mediaId"`
SelfData string `json:"selfData"`
}
type TencentNlpResp struct {
Header *struct {
RequestID string `json:"RequestID"`
SessionID string `json:"SessionID"`
DialogID string `json:"DialogID"`
Code int `json:"Code"`
Message string `json:"Message"`
} `json:"Header"`
Payload *struct {
Semantic *struct {
Query string `json:"Query"`
SessionStatus int `json:"SessionStatus"`
TurnType string `json:"TurnType"`
Results []struct {
Nlu *struct {
Domain string `json:"Domain"`
Intent string `json:"Intent"`
// Slots []struct {
// Name string `json:"Name"`
// Value string `json:"Value"`
// RawValue string `json:"RawValue"`
// Type string `json:"Type"`
// } `json:"Slots"`
Slots []map[string]interface{} `json:"Slots"`
Confidence float32 `json:"Confidence"`
} `json:"Nlu"`
Dm *struct {
SessionID string `json:"SessionID"`
Text string `json:"Text"`
Skill *struct {
Version string `json:"Version"`
ExecutionOrder string `json:"ExecutionOrder"`
AskCount int `json:"AskCount"`
CacheTTS int `json:"CacheTTS"`
Operation struct {
Control struct {
Name string `json:"Name"`
Param interface{} `json:"Param"`
} `json:"Control"`
} `json:"Operation"`
Widget *struct {
Name string `json:"Name"`
Type string `json:"Type"`
Data *struct {
ControlInfo *struct {
Version string `json:"version"`
Type string `json:"type"`
TextSpeak string `json:"textSpeak"`
} `json:"controlInfo"`
GlobalInfo struct {
BackgroundImage interface{} `json:"backgroundImage"`
SeeMore string `json:"seeMore"`
} `json:"globalInfo"`
ListItems []ListItem `json:"listItems"`
} `json:"Data"`
Recommend interface{} `json:"Recommend"`
} `json:"Widget"`
Command map[string]interface{} `json:"Command"`
// Command struct {
// Name string `json:"Name"`
// Param interface{} `json:"Param"`
// TextList interface{} `json:"TextList"`
// Inner interface{} `json:"Inner"`
// } `json:"Command"`
} `json:"Skill"`
IsLocalEntity int `json:"IsLocalEntity"`
SessionComplete int `json:"SessionComplete"`
} `json:"Dm"`
Status struct {
Code int `json:"Code"`
Msg string `json:"Msg"`
} `json:"Status"`
} `json:"Results"`
} `json:"Semantic"`
} `json:"Payload"`
}
func (this *TencentNlpResp) CheckHeader() bool {
if this.Header == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckPayload() bool {
if this.Payload == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckSemantic() bool {
if !this.CheckPayload() || this.Payload.Semantic == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckResults() bool {
if !this.CheckSemantic() || len(this.Payload.Semantic.Results) == 0 {
return false
}
return true
}
func (this *TencentNlpResp) CheckNlu() bool {
if !this.CheckResults() || this.Payload.Semantic.Results[0].Nlu == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckDm() bool {
if !this.CheckResults() || this.Payload.Semantic.Results[0].Dm == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckSkill() bool {
if !this.CheckDm() || this.Payload.Semantic.Results[0].Dm.Skill == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckWidget() bool {
if !this.CheckDm() || this.Payload.Semantic.Results[0].Dm.Skill.Widget == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckWidgetData() bool {
if !this.CheckWidget() || this.Payload.Semantic.Results[0].Dm.Skill.Widget.Data == nil {
return false
}
return true
}
func (this *TencentNlpResp) CheckControlInfo() bool {
if !this.CheckWidgetData() || this.Payload.Semantic.Results[0].Dm.Skill.Widget.Data.ControlInfo == nil {
return false
}
return true
}
func (this *TencentNlpResp) GetCode() int {
return this.Header.Code
}
func (this *TencentNlpResp) GetMessage() string {
return this.Header.Message
}
func (this *TencentNlpResp) GetDomain() string {
return this.Payload.Semantic.Results[0].Nlu.Domain
}
func (this *TencentNlpResp) GetIntent() string {
return this.Payload.Semantic.Results[0].Nlu.Intent
}
func (this *TencentNlpResp) GetSessionComplete() int {
return this.Payload.Semantic.Results[0].Dm.SessionComplete
}
func (this *TencentNlpResp) GetSessionID() string {
return this.Header.SessionID
}
func (this *TencentNlpResp) GetListItems() []ListItem {
return this.Payload.Semantic.Results[0].Dm.Skill.Widget.Data.ListItems
}
func (this *TencentNlpResp) GetSlots() []map[string]interface{} {
return this.Payload.Semantic.Results[0].Nlu.Slots
}
func (this *TencentNlpResp) GetText() string {
return this.Payload.Semantic.Results[0].Dm.Text
}
func (this *TencentNlpResp) GetType() string {
return this.Payload.Semantic.Results[0].Dm.Skill.Widget.Data.ControlInfo.Type
}
func (this *TencentNlpResp) GetCommand() map[string]interface{} {
return this.Payload.Semantic.Results[0].Dm.Skill.Command
}
type Command struct {
Name string `json:"Name"`
Param map[string]string `json:"Param"`
TextList []struct {
Status string `json:"Status"`
Text string `json:"Text"`
PlaceholderList []string `json:"PlaceholderList"`
} `json:"TextList"`
Inner interface{} `json:"Inner"`
}
type TencentNlpWsRespNative struct {
Name string `json:"Name"`
SeqID string `json:"SeqID"`
Timeout int `json:"Timeout"`
Param map[string]string `json:"Param"`
}
// wss 返回结果
type TencentNlpWsResp struct {
Header *struct {
RequestID string `json:"RequestID"`
DialogID string `json:"DialogID"`
Code int `json:"Code"`
Message string `json:"Message"`
} `json:"Header"`
Payload *struct {
ResponseType string `json:"ResponseType"`
Data *struct {
Class string `json:"Class"`
AssistantStatus string `json:"AssistantStatus"`
// VirtualMan string `json:"VirtualMan"` // TODO: object类型, 说明文档错误
Semantic *struct {
Query string `json:"Query"`
RewrittenQuery string `json:"RewrittenQuery"`
SessionStatus int `json:"SessionStatus"`
TurnType string `json:"TurnType"`
IsNeedShield int `json:"IsNeedShield"`
Inner struct {
SegmentID int `json:"SegmentID"`
IsNeedShield int `json:"IsNeedShield"`
// CompoundQuery.IsCompoundQuery // TODO: 说明文档中有, 实际返回缺失该字段
// CompoundQuery.Confidence // TODO: 说明文档中有, 实际返回缺失该字段
} `json:"Inner"`
Results []struct {
Nlu *struct {
Domain string `json:"Domain"`
Intent string `json:"Intent"`
IsNeedShield int `json:"IsNeedShield"`
Inner struct {
SessionComplete int `json:"SessionComplete"`
Confidence float32 `json:"Confidence"`
IsLocalEntity int `json:"IsLocalEntity"`
IsNeedShield int `json:"IsNeedShield"`
} `json:"Inner"`
Slots []map[string]interface{} `json:"Slots"`
// Confidence float32 `json:"Confidence"`
} `json:"Nlu"`
Dm *struct {
SessionID string `json:"SessionID"`
Text string `json:"Text"`
Skill *struct {
Version string `json:"Version"`
ExecutionOrder string `json:"ExecutionOrder"`
AskCount int `json:"AskCount"`
CacheTTS int `json:"CacheTTS"`
Operation struct {
Control struct {
Name string `json:"Name"`
Param interface{} `json:"Param"`
} `json:"Control"`
} `json:"Operation"`
Widget *struct {
Name string `json:"Name"`
Type string `json:"Type"`
Data *struct {
ControlInfo *struct {
Version string `json:"version"`
Type string `json:"type"`
TextSpeak string `json:"textSpeak"`
} `json:"controlInfo"`
GlobalInfo struct {
BackgroundImage interface{} `json:"backgroundImage"`
SeeMore string `json:"seeMore"`
} `json:"globalInfo"`
ListItems []ListItem `json:"listItems"`
} `json:"Data"`
Recommend interface{} `json:"Recommend"`
} `json:"Widget"`
// Command map[string]interface{} `json:"Command"`
Command Command `json:"Command"`
} `json:"Skill"`
IsLocalEntity int `json:"IsLocalEntity"`
SessionComplete int `json:"SessionComplete"` // TODO: 无该字段
} `json:"Dm"`
Status struct {
Code int `json:"Code"`
Msg string `json:"Msg"`
} `json:"Status"`
} `json:"Results"`
} `json:"Semantic"`
// SemanticID // TODO: 说明文档缺少该字段的说明
Native *TencentNlpWsRespNative `json:"Native"`
} `json:"Data"`
CommType string `json:"CommType"`
SDKExtend struct {
} `json:"SDKExtend"`
} `json:"Payload"`
}
func (this *TencentNlpWsResp) CheckHeader() bool {
if this.Header == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckPayload() bool {
if this.Payload == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckData() bool {
if !this.CheckPayload() || this.Payload.Data == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckNative() bool {
if !this.CheckData() || this.Payload.Data.Native == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckSemantic() bool {
if !this.CheckData() || this.Payload.Data.Semantic == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckResults() bool {
if !this.CheckSemantic() || len(this.Payload.Data.Semantic.Results) == 0 {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckNlu() bool {
if !this.CheckResults() || this.Payload.Data.Semantic.Results[0].Nlu == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckDm() bool {
if !this.CheckResults() || this.Payload.Data.Semantic.Results[0].Dm == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckSkill() bool {
if !this.CheckDm() || this.Payload.Data.Semantic.Results[0].Dm.Skill == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckWidget() bool {
if !this.CheckDm() || this.Payload.Data.Semantic.Results[0].Dm.Skill.Widget == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckWidgetData() bool {
if !this.CheckWidget() || this.Payload.Data.Semantic.Results[0].Dm.Skill.Widget.Data == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) CheckControlInfo() bool {
if !this.CheckWidgetData() || this.Payload.Data.Semantic.Results[0].Dm.Skill.Widget.Data.ControlInfo == nil {
return false
}
return true
}
func (this *TencentNlpWsResp) GetCode() int {
return this.Header.Code
}
func (this *TencentNlpWsResp) GetMessage() string {
return this.Header.Message
}
func (this *TencentNlpWsResp) GetDomain() string {
return this.Payload.Data.Semantic.Results[0].Nlu.Domain
}
func (this *TencentNlpWsResp) GetIntent() string {
return this.Payload.Data.Semantic.Results[0].Nlu.Intent
}
func (this *TencentNlpWsResp) GetSessionComplete() int {
return this.Payload.Data.Semantic.Results[0].Dm.SessionComplete
}
func (this *TencentNlpWsResp) GetListItems() []ListItem {
return this.Payload.Data.Semantic.Results[0].Dm.Skill.Widget.Data.ListItems
}
func (this *TencentNlpWsResp) GetSlots() []map[string]interface{} {
return this.Payload.Data.Semantic.Results[0].Nlu.Slots
}
func (this *TencentNlpWsResp) GetText() string {
return this.Payload.Data.Semantic.Results[0].Dm.Text
}
func (this *TencentNlpWsResp) GetType() string {
return this.Payload.Data.Semantic.Results[0].Dm.Skill.Widget.Data.ControlInfo.Type
}
func (this *TencentNlpWsResp) GetCommand() Command {
return this.Payload.Data.Semantic.Results[0].Dm.Skill.Command
}
func (this *TencentNlpWsResp) GetQuery() string {
return this.Payload.Data.Semantic.Query
}
func (this *TencentNlpWsResp) GetResponseType() string {
return this.Payload.ResponseType
}
func (this *TencentNlpWsResp) GetClass() string {
return this.Payload.Data.Class
}
func (this *TencentNlpWsResp) GetNativate() *TencentNlpWsRespNative {
return this.Payload.Data.Native
}
func (this *TencentNlpWsResp) GetSessionStatus() int {
return this.Payload.Data.Semantic.SessionStatus
}
This diff is collapsed.
package consul
import (
"errors"
"fmt"
"math/rand"
"github.com/hashicorp/consul/api"
)
// 从consul中获取服务address port
func GetService(url, token, serviceName, tag string) []*api.ServiceEntry {
var lastIndex uint64
config := api.DefaultConfig()
config.Address = url
config.Token = token
client, err := api.NewClient(config)
if err != nil {
panic(err)
}
services, metainfo, err := client.Health().Service(serviceName, tag, true, &api.QueryOptions{
WaitIndex: lastIndex, // 同步点,这个调用将一直阻塞,直到有新的更新
})
if err != nil {
panic(fmt.Sprintf("error retrieving instances from Consul:%v", err))
}
lastIndex = metainfo.LastIndex
return services
}
// 从consul中获取服务address port
func GetService2(client *api.Client, serviceName, tag string) []*api.ServiceEntry {
var lastIndex uint64
services, metainfo, err := client.Health().Service(serviceName, tag, true, &api.QueryOptions{
WaitIndex: lastIndex, // 同步点,这个调用将一直阻塞,直到有新的更新
})
if err != nil {
}
lastIndex = metainfo.LastIndex
return services
}
type serviceInfo struct {
serviceEntry []*api.ServiceEntry
indexs []int
index int
}
func newServiceInfo(serviceEntry []*api.ServiceEntry) *serviceInfo {
serviceInfo := &serviceInfo{
serviceEntry: serviceEntry,
index: -1,
indexs: make([]int, 0, len(serviceEntry)),
}
serviceInfo.initIndexs()
serviceInfo.initIndex()
return serviceInfo
}
func (s *serviceInfo) IsExist() bool {
if s == nil {
return false
}
if len(s.indexs) > 0 {
return true
} else {
return false
}
}
func (s *serviceInfo) initIndexs() {
for i := 0; i < len(s.serviceEntry); i++ {
if s.serviceEntry[i] != nil {
s.indexs = append(s.indexs, i)
}
}
}
func (s *serviceInfo) initIndex() {
if s.index >= 0 {
return
}
if s.IsExist() {
if len(s.indexs) == 1 {
s.index = s.indexs[0]
return
}
s.index = s.indexs[rand.Intn(len(s.indexs))]
}
}
func (s *serviceInfo) GetAddress() string {
return s.serviceEntry[s.index].Service.Address
}
func (s *serviceInfo) GetPort() int {
return s.serviceEntry[s.index].Service.Port
}
type ConsulObj struct {
client *api.Client
}
func NewConsulObj(url, token string) (*ConsulObj, error) {
config := api.DefaultConfig()
config.Address = url
config.Token = token
client, err := api.NewClient(config)
if err != nil {
return nil, err
}
return &ConsulObj{client: client}, nil
}
func (s *ConsulObj) GetClient() *api.Client {
if s == nil {
return nil
}
return s.client
}
func (s *ConsulObj) GetService(serviceName, tag string) (*serviceInfo, error) {
services, _, err := s.client.Health().Service(serviceName, tag, true, nil)
if err != nil {
return nil, err
}
// fmt.Println(len(services))
if len(services) == 0 {
return nil, errors.New("services is not exist.")
}
return newServiceInfo(services), nil
}
package consul
import (
"context"
"google.golang.org/grpc/health/grpc_health_v1"
)
type HealthImpl struct{}
func (h *HealthImpl) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
return &grpc_health_v1.HealthCheckResponse{
Status: grpc_health_v1.HealthCheckResponse_SERVING,
}, nil
}
func (h *HealthImpl) Watch(request *grpc_health_v1.HealthCheckRequest, server grpc_health_v1.Health_WatchServer) error {
return nil
}
package consul
import (
"github.com/hashicorp/consul/api"
)
func GetKV(url, token string, paths ...string) []byte {
config := api.DefaultConfig()
config.Address = url
config.Token = token
client, err := api.NewClient(config)
if err != nil {
panic(err)
}
var kvContent []byte
for _, path := range paths {
kv, _, err := client.KV().Get(path, nil)
if err != nil {
panic(err)
}
if len(kvContent) > 0 {
kvContent = append(kvContent, []byte("\n\n")...)
}
kvContent = append(kvContent, kv.Value...)
}
return kvContent
}
package consul
import (
"fmt"
"speech-nlu-parse/pkg/util"
"time"
"github.com/hashicorp/consul/api"
)
type ConsulSettingS struct {
RunMode string
Name string
Tag []string
IP string
Port int
ConsulAddr string
ConsulToken string
Interval time.Duration
Deregister time.Duration
}
func RegisterService(cs *ConsulSettingS) error {
consulConfig := api.DefaultConfig()
consulConfig.Address = cs.ConsulAddr
consulConfig.Token = cs.ConsulToken // 增加token
if cs.IP == "" {
cs.IP = util.GetLocalIP()
}
client, err := api.NewClient(consulConfig)
if err != nil {
fmt.Printf("new client error: %v\n", err)
return err
}
agent := client.Agent()
reg := &api.AgentServiceRegistration{
ID: fmt.Sprintf("%v-%v-%v", cs.Name, cs.IP, cs.Port), //服务结点的名称
Name: cs.Name, //服务名称
Tags: cs.Tag, //tag, 不可以为空
Port: cs.Port, //服务端口
Address: cs.IP, //服务IP
Check: &api.AgentServiceCheck{ //健康检查
Interval: cs.Interval.String(), //健康检查间隔
GRPC: fmt.Sprintf("%v:%v/%v", cs.IP, cs.Port, cs.Name), //grpc支持,执行健康检查的地址,service会传到Health.Check函数中
DeregisterCriticalServiceAfter: cs.Deregister.String(), //注销时间,相当于过期时间
},
}
if err := agent.ServiceRegister(reg); err != nil {
fmt.Printf("service register error: %v\n", err)
return err
}
return nil
}
package consul
import (
"errors"
"fmt"
"log"
"regexp"
"sync"
"github.com/hashicorp/consul/api"
"google.golang.org/grpc/resolver"
)
const (
defaultPort = "8500"
)
var (
errMissingAddr = errors.New("consul resolver: missing address")
errAddrMisMatch = errors.New("consul resolver: invalied uri")
errEndsWithColon = errors.New("consul resolver: missing port after port-separator colon")
regexConsul, _ = regexp.Compile("^([A-z0-9.]+)(:[0-9]{1,5})?/([A-z_-]+)$") // 发现规则存在问题
)
func Init() {
fmt.Println("calling consul init")
resolver.Register(NewBuilder())
}
type consulBuilder struct{}
func NewBuilder() resolver.Builder {
return &consulBuilder{}
}
func (cb *consulBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
host, port, name, err := parseTarget(fmt.Sprintf("%s/%s", target.Authority, target.Endpoint))
if err != nil {
return nil, err
}
fmt.Println(fmt.Sprintf("consul service ==> host:%s, port%s, name:%s", host, port, name))
cr := &consulResolver{
address: fmt.Sprintf("%s%s", host, port),
name: name,
cc: cc,
disableServiceConfig: opts.DisableServiceConfig,
lastIndex: 0,
}
cr.wg.Add(1)
go cr.watcher()
return cr, nil
}
func (cb *consulBuilder) Scheme() string {
return "consul"
}
type consulResolver struct {
address string
wg sync.WaitGroup
cc resolver.ClientConn
name string
disableServiceConfig bool
lastIndex uint64
}
func (cr *consulResolver) watcher() {
fmt.Printf("calling [%s] consul watcher\n", cr.name)
config := api.DefaultConfig()
config.Address = cr.address
client, err := api.NewClient(config)
if err != nil {
fmt.Printf("error create consul client: %v\n", err)
return
}
fmt.Printf("resolver connect to consul:%v,%v,%v\n", cr.address, cr.name, cr.lastIndex)
for {
services, metainfo, err := client.Health().ServiceMultipleTags(cr.name, []string{},
true, &api.QueryOptions{WaitIndex: cr.lastIndex})
if err != nil {
log.Printf("error retrieving instances from Consul: %v", err)
}
cr.lastIndex = metainfo.LastIndex
var newAddrs []resolver.Address
for _, service := range services {
addr := fmt.Sprintf("%v:%v", service.Service.Address, service.Service.Port)
newAddrs = append(newAddrs, resolver.Address{
Addr: addr,
//type:不能是grpclb,grpclb在处理链接时会删除最后一个链接地址,不用设置即可 详见=> balancer_conn_wrappers => updateClientConnState
ServerName: service.Service.Service,
})
}
fmt.Printf("adding service addrs:%v\n", newAddrs)
//cr.cc.NewAddress(newAddrs)
//cr.cc.NewServiceConfig(cr.name)
cr.cc.UpdateState(resolver.State{Addresses: newAddrs})
}
}
func (cr *consulResolver) ResolveNow(opt resolver.ResolveNowOptions) {}
func (cr *consulResolver) Close() {}
func parseTarget(target string) (host, port, name string, err error) {
if target == "" {
return "", "", "", errMissingAddr
}
if !regexConsul.MatchString(target) {
return "", "", "", errAddrMisMatch
}
groups := regexConsul.FindStringSubmatch(target)
host = groups[1]
port = groups[2]
name = groups[3]
if port == "" {
port = defaultPort
}
return host, port, name, nil
}
package errCode
var (
Success = NewError(0, "成功")
TargetDeviceNotExist = NewError(1200, "目标设备不存在")
TargetDeviceNotOnline = NewError(1201, "目标设备不在线")
TargetDeviceNotResponding = NewError(1202, "目标设备未响应")
MultipleTargetDevices = NewError(1203, "存在多个目标设备")
MutuallyExclusiveFunction = NewError(1204, "存在互斥功能")
AlreadyTarget = NewError(1205, "已是目标状态")
TargetDeviceNotThisFunction = NewError(1206, "目标设备没有该功能")
RequestFormatError = NewError(1401, "请求格式错误")
InformationMissing = NewError(1402, "用户或设备信息缺失")
IdentityAuthenticationFailed = NewError(1403, "身份认证失败")
InternalServiceError = NewError(1500, "内部服务错误")
IotInterfaceAccessTimeout = NewError(1501, "iot接口访问超时")
IotInterfaceReturnsError = NewError(1502, "iot接口返回错误")
DingdangInterfaceTimeout = NewError(1503, "叮当服务超时")
DingdangInterfaceRequestFrequency = NewError(1504, "叮当服务请求过快")
)
package errCode
import "fmt"
// TODO 请在使用时手动添加 json 注解
type Error struct {
code int32
msg string
}
var codes = map[int32]string{}
func NewError(code int32, msg string) *Error {
if _, ok := codes[code]; ok {
panic(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code))
}
codes[code] = msg
return &Error{code: code, msg: msg}
}
func (e *Error) Code() int32 {
return e.code
}
func (e *Error) Msg() string {
return e.msg
}
package logger
import (
"context"
"fmt"
"strings"
"time"
"github.com/jinzhu/gorm"
)
const (
zapLoggerKey = "zap_logger"
contextGormKey = "added_value"
loggerGormKey = "gorm_logger"
)
// WithContext 用来将需要在sql日志记录的附加的值,将key设置为gorm-added
// 举例:context.WithValue(parentCtx, "gorm-added", <value>)
func WithContext(ctx context.Context, db *gorm.DB) *gorm.DB {
if ctx == nil {
return db
}
return db.Set(contextGormKey, ctx)
}
// 为数据库添加回调方法--添加数据库日志追踪
func AddGormCallbacks(db *gorm.DB, logger *Logger) {
callbacks := newCallbacks()
//设置logger用于记录数据库操作日志
//此处不能使用Set方法存储
//InstantSet方法才能影响当前的数据库标识
db.InstantSet(zapLoggerKey, logger)
registerCallbacks(db, "create", callbacks)
registerCallbacks(db, "query", callbacks)
registerCallbacks(db, "update", callbacks)
registerCallbacks(db, "delete", callbacks)
registerCallbacks(db, "row_query", callbacks)
}
type callbacks struct{}
func newCallbacks() *callbacks {
return &callbacks{}
}
func (c *callbacks) beforeCreate(scope *gorm.Scope) { c.before(scope) }
func (c *callbacks) afterCreate(scope *gorm.Scope) { c.after(scope, "INSERT") }
func (c *callbacks) beforeQuery(scope *gorm.Scope) { c.before(scope) }
func (c *callbacks) afterQuery(scope *gorm.Scope) { c.after(scope, "SELECT") }
func (c *callbacks) beforeUpdate(scope *gorm.Scope) { c.before(scope) }
func (c *callbacks) afterUpdate(scope *gorm.Scope) { c.after(scope, "UPDATE") }
func (c *callbacks) beforeDelete(scope *gorm.Scope) { c.before(scope) }
func (c *callbacks) afterDelete(scope *gorm.Scope) { c.after(scope, "DELETE") }
func (c *callbacks) beforeRowQuery(scope *gorm.Scope) { c.before(scope) }
func (c *callbacks) afterRowQuery(scope *gorm.Scope) { c.after(scope, "") }
func (c *callbacks) before(scope *gorm.Scope) {
fields := Fields{"begin_time": time.Now().Format("2006-01-02 15:04:05.000")}
ctx, ok := scope.Get(contextGormKey)
if ok {
fields["context_value"] = ctx.(context.Context).Value("gorm-added")
}
scope.Set(loggerGormKey, fields)
}
func (c *callbacks) after(scope *gorm.Scope, operation string) {
zapLogger, ok := scope.Get(zapLoggerKey)
if !ok {
return
}
logger := zapLogger.(*Logger)
if operation == "" {
operation = strings.ToUpper(strings.Split(scope.SQL, " ")[0])
}
val, ok := scope.Get(loggerGormKey)
if !ok {
return
}
fields := val.(Fields)
fields["DBStatement"] = scope.SQL
fields["DB.SQLVar"] = scope.SQLVars
scope.SQL = strings.Replace(scope.SQL, "?", "%v", -1)
fields["DB.SQL"] = fmt.Sprintf(scope.SQL, scope.SQLVars...)
fields["db.table"] = scope.TableName()
fields["db.method"] = operation
fields["db.hasErr"] = scope.HasError()
fields["db.count"] = scope.DB().RowsAffected
logger.WithFields(fields).Info("GORM TRACE")
}
func registerCallbacks(db *gorm.DB, name string, c *callbacks) {
beforeName := fmt.Sprintf("tracing:%v_before", name)
afterName := fmt.Sprintf("tracing:%v_after", name)
gormCallbackName := fmt.Sprintf("gorm:%v", name)
// gorm does some magic, if you pass CallbackProcessor here - nothing works
switch name {
case "create":
db.Callback().Create().Before(gormCallbackName).Register(beforeName, c.beforeCreate)
db.Callback().Create().After(gormCallbackName).Register(afterName, c.afterCreate)
case "query":
db.Callback().Query().Before(gormCallbackName).Register(beforeName, c.beforeQuery)
db.Callback().Query().After(gormCallbackName).Register(afterName, c.afterQuery)
case "update":
db.Callback().Update().Before(gormCallbackName).Register(beforeName, c.beforeUpdate)
db.Callback().Update().After(gormCallbackName).Register(afterName, c.afterUpdate)
case "delete":
db.Callback().Delete().Before(gormCallbackName).Register(beforeName, c.beforeDelete)
db.Callback().Delete().After(gormCallbackName).Register(afterName, c.afterDelete)
case "row_query":
db.Callback().RowQuery().Before(gormCallbackName).Register(beforeName, c.beforeRowQuery)
db.Callback().RowQuery().After(gormCallbackName).Register(afterName, c.afterRowQuery)
}
}
package logger
import (
"fmt"
"io"
"os"
"runtime"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type Fields map[string]interface{}
type Logger struct {
newLogger *zap.Logger
fields Fields
callers []string
}
func NewLogger(hook io.Writer, mode string, serviceName string) *Logger {
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "service",
CallerKey: "tag",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写编码器
EncodeTime: func(time time.Time, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(time.Format("2006-01-02 15:04:05"))
}, // ISO8601 UTC 时间格式
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder, // 全路径编码器
EncodeName: zapcore.FullNameEncoder,
}
// 设置日志级别
atomicLevel := zap.NewAtomicLevel()
var syncer zapcore.WriteSyncer
if mode == "debug" { // debug模式
// 设置日志级别
atomicLevel.SetLevel(zap.DebugLevel)
// 输出到控制台
syncer = zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout))
} else {
atomicLevel.SetLevel(zap.InfoLevel)
// 输出到文件
// syncer = zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook)) //打印到文件 --打印到控制台:zapcore.AddSync(os.Stdout)
syncer = zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)) // 打印到控制台
}
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig), // 编码器配置
// zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook)), //打印到文件 --打印到控制台:zapcore.AddSync(os.Stdout)
syncer,
atomicLevel, // 日志级别
)
//输出文件和行号,前提是配置对象encoderConfig中必须设有CallerKey字段
caller := zap.AddCaller()
//由于再次封装日志,因此需要打印上一级的调用,1表示向上跳一级
callerSkip := zap.AddCallerSkip(1)
if mode == "debug" {
//开启开发模式
return &Logger{
newLogger: zap.New(core, caller, callerSkip, zap.Development()).Named(serviceName),
}
}
return &Logger{
newLogger: zap.New(core, caller, callerSkip).Named(serviceName),
}
}
func (l *Logger) clone() *Logger { //防止并发时的数据脏乱
nl := *l
return &nl
}
func (l *Logger) WithFields(f Fields) *Logger {
ll := l.clone()
if ll.fields == nil {
ll.fields = make(Fields)
}
for k, v := range f {
ll.fields[k] = v
}
return ll
}
func (l *Logger) WithCallersFrames() *Logger {
maxCallerDepth := 25
minCallerDepth := 1
callers := []string{}
pcs := make([]uintptr, maxCallerDepth)
depth := runtime.Callers(minCallerDepth, pcs)
frames := runtime.CallersFrames(pcs[:depth])
for frame, more := frames.Next(); more; frame, more = frames.Next() {
callers = append(callers, fmt.Sprintf("%s: %d %s", frame.File, frame.Line, frame.Function))
if !more {
break
}
}
ll := l.clone()
ll.callers = callers
return ll
}
func (l *Logger) Debug(msg string) {
if l.fields != nil {
l.newLogger.Debug(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Debug(msg)
}
func (l *Logger) Debugf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
if l.fields != nil {
l.newLogger.Debug(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Debug(msg)
}
func (l *Logger) Info(msg string) {
if l.fields != nil {
l.newLogger.Info(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Info(msg)
}
func (l *Logger) Infof(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
if l.fields != nil {
l.newLogger.Info(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Info(msg)
}
func (l *Logger) Warn(msg string) {
if l.fields != nil {
l.newLogger.Warn(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Warn(msg)
}
func (l *Logger) Warnf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
if l.fields != nil {
l.newLogger.Warn(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Warn(msg)
}
func (l *Logger) Error(msg string) {
if l.fields != nil {
l.newLogger.Error(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Error(msg)
}
func (l *Logger) Errorf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
if l.fields != nil {
l.newLogger.Error(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Error(msg)
}
func (l *Logger) Fatal(msg string) {
if l.fields != nil {
l.newLogger.Fatal(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Fatal(msg)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
if l.fields != nil {
l.newLogger.Fatal(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Fatal(msg)
}
func (l *Logger) Panic(msg string) {
if l.fields != nil {
l.newLogger.Panic(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Panic(msg)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
if l.fields != nil {
l.newLogger.Panic(msg, zap.Any("field", l.fields))
return
}
ll := l.clone()
ll.newLogger.Panic(msg)
}
{
"macWifi": "Hello",
"macVoice": "Hello",
"query": "第一个",
"ip": "120.198.22.24",
"originQuery": "十四十四新闻",
"mid": "10f05",
"requestId": "00e04cb723c220231104163611",
"vender": "87654321",
"appKey": "a8814080829e11eda5c8e38bb1008e50",
"accessToken": "5659be84ea8c43d9ab9243b4a40cb3ee",
"qua": "testv1",
"auth": "",
"dsn": "",
"exist": false,
"tRequestId": "gz1c02944e16990869750923801",
"requestType": "semantic.text",
"commType": "SIMPLEX",
"native": {},
"sdkExtend": {},
"addressInfo": {},
"language":"chinese"
}
\ No newline at end of file
{
"macWifi": "Hello",
"ip": "120.198.22.24",
"mid": "10f05",
"requestId": "62971cb0-3b0f-11ee-9149-02420a0ac81a",
"vender": "87654321",
"appKey": "a8814080829e11eda5c8e38bb1008e50",
"accessToken": "5659be84ea8c43d9ab9243b4a40cb3ee",
"qua": "testv1",
"auth": "",
"dsn": "",
"exist": "0",
"trequestid": "",
"sessionid": "62971cb0-3b0f-11ee-9149-02420a0ac"
}
\ No newline at end of file
This diff is collapsed.
syntax = "proto3";
package proto; //新增一个可选的package声明符,用来防止不同的消息类型有命名冲突
option go_package = "./pkg/proto;proto"; //这个选项表明生成go结构体所在的包
//import "google/protobuf/any.proto";
message MusicSpotRequest {
message AppInfo {
string requestId = 1;
}
message DevInfo {
string mac = 1;
string subMac = 2; // 子设备mac,中控变成了子设备
string mid = 3; // mid
string vender = 4; // 细分码,用于设备没授权的情况下给默认boot
string hid = 5;// 模组id,优先级高于细分码 - 若能匹配,优先使用
string homeId = 6; // 设备所属家庭id
string userId = 7; // 用户id
}
AppInfo appInfo = 1;
DevInfo devInfo = 2;
message Data {
string query = 1;
int64 limit = 2; // 目前不生效
}
Data data = 3;
}
message MusicSpotResponse {
message Status {
int32 code = 1;
string msg = 2;
}
message SourceItem {
string mediaId = 1;
string singer = 2;
string song = 3;
}
message Data{
repeated SourceItem listItems = 1;
}
Status status = 1;
Data data = 2;
}
service MusicSpot {
rpc GetMusicList(MusicSpotRequest) returns (MusicSpotResponse); // 获取音乐列表
}
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.1
// source: pkg/proto/music-spot.proto
package proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
MusicSpot_GetMusicList_FullMethodName = "/proto.MusicSpot/GetMusicList"
)
// MusicSpotClient is the client API for MusicSpot service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MusicSpotClient interface {
GetMusicList(ctx context.Context, in *MusicSpotRequest, opts ...grpc.CallOption) (*MusicSpotResponse, error)
}
type musicSpotClient struct {
cc grpc.ClientConnInterface
}
func NewMusicSpotClient(cc grpc.ClientConnInterface) MusicSpotClient {
return &musicSpotClient{cc}
}
func (c *musicSpotClient) GetMusicList(ctx context.Context, in *MusicSpotRequest, opts ...grpc.CallOption) (*MusicSpotResponse, error) {
out := new(MusicSpotResponse)
err := c.cc.Invoke(ctx, MusicSpot_GetMusicList_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MusicSpotServer is the server API for MusicSpot service.
// All implementations must embed UnimplementedMusicSpotServer
// for forward compatibility
type MusicSpotServer interface {
GetMusicList(context.Context, *MusicSpotRequest) (*MusicSpotResponse, error)
mustEmbedUnimplementedMusicSpotServer()
}
// UnimplementedMusicSpotServer must be embedded to have forward compatible implementations.
type UnimplementedMusicSpotServer struct {
}
func (UnimplementedMusicSpotServer) GetMusicList(context.Context, *MusicSpotRequest) (*MusicSpotResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMusicList not implemented")
}
func (UnimplementedMusicSpotServer) mustEmbedUnimplementedMusicSpotServer() {}
// UnsafeMusicSpotServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MusicSpotServer will
// result in compilation errors.
type UnsafeMusicSpotServer interface {
mustEmbedUnimplementedMusicSpotServer()
}
func RegisterMusicSpotServer(s grpc.ServiceRegistrar, srv MusicSpotServer) {
s.RegisterService(&MusicSpot_ServiceDesc, srv)
}
func _MusicSpot_GetMusicList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MusicSpotRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MusicSpotServer).GetMusicList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MusicSpot_GetMusicList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MusicSpotServer).GetMusicList(ctx, req.(*MusicSpotRequest))
}
return interceptor(ctx, in, info, handler)
}
// MusicSpot_ServiceDesc is the grpc.ServiceDesc for MusicSpot service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var MusicSpot_ServiceDesc = grpc.ServiceDesc{
ServiceName: "proto.MusicSpot",
HandlerType: (*MusicSpotServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetMusicList",
Handler: _MusicSpot_GetMusicList_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/proto/music-spot.proto",
}
This diff is collapsed.
syntax = "proto3";
package proto; //新增一个可选的package声明符,用来防止不同的消息类型有命名冲突
option go_package = "./pkg/proto;proto"; //这个选项表明生成go结构体所在的包
// import "google/protobuf/any.proto";
message Status {
int32 code = 1;
string msg = 2;
}
// 经纬度
message SemanticRequestLbs {
double longitude = 1;
double latitude = 2;
}
message AddressInfo {
string province = 1;
string city = 2;
string county = 3;
}
message SemanticRequest {
string macWifi = 1;
string macVoice = 2;
string query = 3;
string ip = 4;
string testID = 5;
string originQuery = 6;
string mid = 7;
string requestId = 8;
SemanticRequestLbs lbs = 9;
string vender = 10;
string appKey = 11; // 云小微
string accessToken = 12;
string qua = 13;
string auth = 14;
string dsn = 15;
string guid = 16;
bool exist = 17; // 授权信息是否存在
string tRequestId = 18; // 腾讯v2的requestId
string requestType = 19; // 腾讯nlp websocket RequestType
string commType = 20; // 腾讯nlp websocket CommType
string characterID = 21; // 腾讯nlp websocket Common.CharacterID
string event = 22; // 腾讯nlp websocket Semantic.Event
message Native {
string name = 1;
string seqID = 2;
int32 code = 3;
string message = 4;
string dataVersion = 5;
message Data {
// TODO: Native API协议数据, 由云端和客户端一起定义
}
Data data = 6;
}
Native native = 23; // 腾讯nlp websocket Payload.Nativate
message SDKExtend {
int32 carDOA = 1;
string lastRspType = 2;
string lastRspResult = 3;
int32 lastSessionStatus = 4;
string lastCompleteCmdResult = 5;
}
SDKExtend sdkExtend = 24; // 腾讯nlp websocket Payload.SDKExtend
AddressInfo addressInfo = 25;
string language = 26;
string homeId = 27;
}
message SemanticResponse {
Status status = 1;
SemanticData data = 2;
}
message SemanticData {
string semanticRespJsonData = 1;
}
service TencentNlu {
rpc TencentNluParse (SemanticRequest) returns (SemanticResponse) {}
rpc TencentNluParseStream (stream SemanticRequest) returns (stream SemanticResponse) {}
}
/*
################## token_search.proto ##################
查询dsn及状态信息
*/
message TokenSearchRequest {
string mac = 1;
string requestId = 2;
string mid = 3; // mid
string vender = 4; // 细分码,用于设备没授权的情况下给默认boot
string homeId = 5; // 设备所属家庭id
string userId = 6; // 用户id
}
/*
data.uriType 取值说明:
QQMusic = 0 // qq音乐
KugouMusic = 1 // 酷狗音乐
data.status 取值说明:
Unassigned = 0 //未分配 没有AppKey 没有token
Assigned = 1 //已分配
Using = 2 //已授权,使用中
Expired = 3 //过期
Invalidation = 4 //异常失效
Unbind = 5 //解除绑定
status.code
200 成功
401 激活错误
403 参数格式
405 设备为激活
*/
message TokenSearchResponse {
message Status {
int32 code = 1;
string msg = 2;
}
message TokenMemo {
string dsn = 1;
string authorization = 2;
string accessToken = 3;
string appKey = 4;
int32 status = 5;
int32 uriType = 6;
string homeId = 7; // 设备所绑定家庭的 homeid
}
Status status = 1;
TokenMemo data = 2;
}
service TokenSearch{
rpc TokenSearchHandle(TokenSearchRequest) returns (TokenSearchResponse);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
package setting
import (
"bytes"
"speech-nlu-parse/pkg/consul"
"github.com/spf13/viper"
)
// TODO consul的地址和token
// var ( // 默认本地环境的地址
// consulURL = "http://172.28.5.39:8500"
// token = "092288b5-824f-854c-39aa-a958afd9a633"
// )
type Setting struct {
vp *viper.Viper
}
func NewSetting(consulAddr, consulToken string, configs ...string) (*Setting, error) {
vp := viper.New()
vp.SetConfigType("yaml")
configContent := consul.GetKV(consulAddr, consulToken, configs...)
err := vp.ReadConfig(bytes.NewBuffer(configContent))
if err != nil {
return nil, err
}
s := &Setting{
vp: vp,
}
return s, nil
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment