中国本土应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
16KB

  1. # -*- coding: utf-8 -*-
  2. import logging
  3. import time
  4. from datetime import datetime, timedelta
  5. from odoo import _, api, fields, models
  6. from odoo.exceptions import UserError
  7. from odoo.addons.oec_im_wecom_api.api.wecom_abstract_api import ApiException
  8. _logger = logging.getLogger(__name__)
  9. class WeComApps(models.Model):
  10. _name = "wecom.apps"
  11. _description = "Wecom Application"
  12. _order = "sequence"
  13. name = fields.Char(
  14. string="Name",
  15. copy=False,
  16. compute="_compute_name",
  17. store=True,
  18. index=True,
  19. ) # 企业应用名称
  20. app_name = fields.Char(
  21. string="Application Name",
  22. translate=True,
  23. copy=True,
  24. ) # 应用名称
  25. company_id = fields.Many2one(
  26. "res.company",
  27. string="Company",
  28. domain="[('is_wecom_organization', '=', True)]",
  29. copy=False,
  30. store=True,
  31. )
  32. # model_ids = fields.Many2many("ir.model", string="Related Model",)
  33. # 应用类型 required=True
  34. type = fields.Selection(
  35. selection=lambda self: self._type_selection_values(),
  36. string="Application Type",
  37. required=True,
  38. copy=True,
  39. default="manage",
  40. )
  41. type_id = fields.Many2one("wecom.app.type", string="Application Types", store=True)
  42. subtype_ids = fields.Many2many(
  43. "wecom.app.subtype",
  44. string="Application Subtype",
  45. )
  46. type_code = fields.Char(
  47. string="Application type code",
  48. store=True,
  49. readonly=True,
  50. compute="_computet_type_code",
  51. )
  52. def write(self, vals):
  53. for app in self:
  54. labels = dict(self.fields_get(allfields=["type"])["type"]["selection"])[
  55. app.type
  56. ]
  57. if "company_id" in vals:
  58. company = app.company_id
  59. vals["name"] = "%s/%s/%s" % (
  60. company.abbreviated_name,
  61. labels,
  62. app.app_name,
  63. )
  64. else:
  65. vals["name"] = "%s/%s" % (labels, app.app_name)
  66. result = super(WeComApps, self).write(vals)
  67. return result
  68. @api.depends("subtype_ids")
  69. def _computet_type_code(self):
  70. """
  71. 计算应用类型代码
  72. """
  73. if self.subtype_ids:
  74. self.type_code = str(self.subtype_ids.mapped("code"))
  75. else:
  76. self.type_code = "[]"
  77. def get_type_code(self):
  78. """
  79. 测试代码
  80. """
  81. type_code = str(self.subtype_ids.mapped("code"))
  82. print(type_code, type(type_code))
  83. @api.onchange("subtype_ids")
  84. def _onchange_subtype_ids(self):
  85. """
  86. 变更子类型
  87. :return:
  88. """
  89. if self.type_id.code == "manage" or self.type_id.code == "base":
  90. if len(self.subtype_ids) > 1:
  91. raise UserError(
  92. _("Only one subtype can be selected for the current app type!")
  93. )
  94. @api.model
  95. def _type_selection_values(self):
  96. models = self.env["wecom.app.type"].sudo().search([]).sorted("sequence")
  97. return [(model.code, model.name) for model in models]
  98. @api.onchange("type")
  99. def _onchange_type(self):
  100. # self.subtype_ids = False
  101. # self.type_code = ""
  102. # if self.type:
  103. # type = self.env["wecom.app.type"].sudo().search([("code", "=", self.type)])
  104. # self.type_id = type
  105. # return {"domain": {"subtype_ids": [("parent_id", "=", type.id)]}}
  106. # else:
  107. # self.type_id = False
  108. # return {"domain": {"subtype": []}}
  109. if self.subtype_ids:
  110. self.type_code = str(self.subtype_ids.mapped("code"))
  111. else:
  112. self.type_code = "[]"
  113. agentid = fields.Integer(string="Agent ID", copy=False) # 企业应用id
  114. secret = fields.Char("Secret", default="", copy=False)
  115. square_logo_url = fields.Char(string="Square Logo", copy=True) # 企业应用方形头像
  116. description = fields.Text(string="Description", translate=True, copy=True) # 企业应用详情
  117. allow_userinfos = fields.Char(
  118. string="Visible range (personnel)", copy=False
  119. ) # 企业应用可见范围(人员),其中包括userid
  120. allow_partys = fields.Char(
  121. string="Visible range (Department)", copy=False
  122. ) # 企业应用可见范围(部门)
  123. allow_tags = fields.Char(string="Visible range (Tag))", copy=False) # 企业应用可见范围(标签)
  124. close = fields.Boolean(string="Disabled", copy=False) # 企业应用是否被停用
  125. redirect_domain = fields.Char(string="Trusted domain name", copy=True) # 企业应用可信域名
  126. report_location_flag = fields.Boolean(
  127. string="Open the geographic location and report", copy=False
  128. ) # 企业应用是否打开地理位置上报 0:不上报;1:进入会话上报;
  129. isreportenter = fields.Boolean(
  130. string="Report user entry event", copy=False
  131. ) # 企业应用是否打开进入会话上报 0:不上报;1:进入会话上报;
  132. home_url = fields.Char(string="Home page", copy=True) # 企业应用主页url
  133. sequence = fields.Integer(default=0, copy=True)
  134. menu_body = fields.Text(
  135. "Application menu data",
  136. translate=True,
  137. default="{}",
  138. copy=False,
  139. )
  140. # 访问令牌
  141. access_token = fields.Char(string="Access Token", readonly=True, copy=False)
  142. token_expiration_time = fields.Datetime(
  143. string="Token Expiration Time", readonly=True, copy=False
  144. )
  145. # 应用的jsapi_ticket
  146. jsapi_ticket = fields.Char(string="JSAPI Ticket", readonly=True, copy=False)
  147. jsapi_ticket_expiration_time = fields.Datetime(
  148. string="JSAPI Ticket Expiration Time", readonly=True, copy=False
  149. )
  150. _sql_constraints = [
  151. (
  152. "company_id_key_uniq",
  153. "unique (company_id,app_name)",
  154. _("The application name of each company must be unique !"),
  155. )
  156. ]
  157. @api.model_create_multi
  158. def create(self, values):
  159. res = super(WeComApps, self).create(values)
  160. if res.subtype_ids:
  161. res.type_code = str(res.subtype_ids.mapped("code"))
  162. else:
  163. res.type_code = "[]"
  164. return res
  165. @api.depends("company_id", "app_name", "type")
  166. def _compute_name(self):
  167. for app in self:
  168. labels = dict(self.fields_get(allfields=["type"])["type"]["selection"])[
  169. app.type
  170. ]
  171. if app.company_id:
  172. app.name = "%s/%s/%s" % (
  173. app.company_id.abbreviated_name,
  174. labels,
  175. app.app_name,
  176. )
  177. else:
  178. app.name = "%s/%s" % (labels, app.app_name)
  179. # 回调服务
  180. app_callback_service_ids = fields.One2many(
  181. "wecom.app_callback_service",
  182. "app_id",
  183. string="Receive event service",
  184. domain="['|', ('active', '=', True), ('active', '=', False)]",
  185. context={"active_test": False},
  186. )
  187. # 应用参数配置
  188. app_config_ids = fields.One2many(
  189. "wecom.app_config",
  190. "app_id",
  191. string="Application Configuration",
  192. # context={
  193. # "default_company_id": lambda self: self.company_id,
  194. # },
  195. ) # 应用参数配置
  196. # ————————————————————————————————————
  197. # 应用回调服务
  198. # ————————————————————————————————————
  199. def generate_service(self):
  200. """
  201. 生成回调服务
  202. :return:
  203. """
  204. code = self.env.context.get("code") # 按钮的传递值
  205. if bool(code):
  206. # 存在按钮的传递值,通过按钮的传递值生成回调服务
  207. self.generate_service_by_code(code)
  208. else:
  209. # 不存在按钮的传递值,通过子类型生成生成回调服务
  210. self.generate_service_by_subtype()
  211. def generate_service_by_subtype(self):
  212. """
  213. 通过子类型生成生成回调服务
  214. """
  215. for record in self.subtype_ids:
  216. self.generate_service_by_code(record.code)
  217. def generate_service_by_code(self, code):
  218. """
  219. 根据code生成回调服务
  220. :param code:
  221. :return:
  222. """
  223. # ————————————————————————————————————
  224. # 应用参数配置
  225. # ————————————————————————————————————
  226. def generate_parameters(self):
  227. """
  228. 生成参数
  229. :return:
  230. """
  231. code = self.env.context.get("code") # 按钮的传递值
  232. if bool(code):
  233. # 存在按钮的传递值,通过按钮的传递值生成回调服务
  234. self.generate_parameters_by_code(code)
  235. else:
  236. # 不存在按钮的传递值,通过子类型生成生成回调服务
  237. self.generate_parameters_by_subtype()
  238. def generate_parameters_by_subtype(self):
  239. """
  240. 通过子类型生成生成参数
  241. """
  242. for record in self.subtype_ids:
  243. self.generate_parameters_by_code(record.code)
  244. def generate_parameters_by_code(self, code):
  245. """
  246. 根据code生成参数
  247. :param code:
  248. :retur
  249. """
  250. # ————————————————————————————————————
  251. # 应用信息
  252. # ————————————————————————————————————
  253. def get_app_info(self):
  254. """
  255. 获取企业应用信息
  256. :param agentid:
  257. :return:
  258. """
  259. for record in self:
  260. try:
  261. wecomapi = self.env["wecom.service_api"].InitServiceApi(
  262. record.company_id.corpid, record.secret
  263. )
  264. response = wecomapi.httpCall(
  265. self.env["wecom.service_api_list"].get_server_api_call("AGENT_GET"),
  266. {"agentid": str(record.agentid)},
  267. )
  268. except ApiException as e:
  269. return self.env["wecomapi.tools.action"].ApiExceptionDialog(
  270. e, raise_exception=True
  271. )
  272. else:
  273. if response["errcode"] == 0:
  274. record.write(
  275. {
  276. "app_name": response["name"],
  277. "square_logo_url": response["square_logo_url"],
  278. "description": response["description"],
  279. "allow_userinfos": response["allow_userinfos"]
  280. if "allow_userinfos" in response
  281. else "{}",
  282. "allow_partys": response["allow_partys"]
  283. if "allow_partys" in response
  284. else "{}",
  285. "allow_tags": response["allow_tags"]
  286. if "allow_tags" in response
  287. else "{}",
  288. "close": response["close"],
  289. "redirect_domain": response["redirect_domain"],
  290. "report_location_flag": response["report_location_flag"],
  291. "isreportenter": response["isreportenter"],
  292. "home_url": response["home_url"],
  293. }
  294. )
  295. # msg = {
  296. # "title": _("Tips"),
  297. # "message": _("Successfully obtained application information!"),
  298. # "sticky": False,
  299. # }
  300. # return self.env["wecomapi.tools.action"].WecomSuccessNotification(msg)
  301. def set_app_info(self):
  302. """
  303. 设置企业应用信息
  304. :param agentid:
  305. :return:
  306. """
  307. # ————————————————————————————————————
  308. # 应用令牌
  309. # ————————————————————————————————————
  310. def get_access_token(self):
  311. """获取企业应用接口调用凭据(令牌)
  312. :return:
  313. """
  314. ir_config = self.env["ir.config_parameter"].sudo()
  315. debug = ir_config.get_param("wecom.debug_enabled")
  316. if debug:
  317. _logger.info(
  318. _("Start getting app [%s] token for company [%s]")
  319. % (self.name, self.company_id.name)
  320. )
  321. try:
  322. oec_im_wecom_api = self.env["wecom.service_api"].InitServiceApi(
  323. self.company_id.corpid, self.secret
  324. )
  325. except ApiException as ex:
  326. return self.env["wecomapi.tools.action"].ApiExceptionDialog(
  327. ex, raise_exception=True
  328. )
  329. # finally:
  330. # if self.token_expiration_time and self.token_expiration_time > datetime.now():
  331. # # 令牌未过期,则直接返回 提示信息
  332. # msg = {
  333. # "title": _("Tips"),
  334. # "message": _("Token is still valid, and no update is required!"),
  335. # "sticky": False,
  336. # }
  337. # return self.env["wecomapi.tools.action"].WecomInfoNotification(msg)
  338. def cron_get_app_token(self):
  339. """
  340. 自动任务定时获取应用token
  341. """
  342. for app in self.search([("company_id", "!=", False)]):
  343. _logger.info(
  344. _(
  345. "Automatic task: start to get the application [%s] token of company [%s]"
  346. )
  347. % (app.name, app.company_id.name)
  348. )
  349. app.get_access_token()
  350. # ————————————————————————————————————
  351. # 应用 jsapi_ticket
  352. # ————————————————————————————————————
  353. def get_app_jsapi_ticket(self):
  354. """
  355. 获取应用的jsapi_ticket
  356. """
  357. ir_config = self.env["ir.config_parameter"].sudo()
  358. debug = ir_config.get_param("wecom.debug_enabled")
  359. if debug:
  360. _logger.info(
  361. _("Start getting app [%s] ticket for company [%s]")
  362. % (self.name, self.company_id.name)
  363. )
  364. try:
  365. if (
  366. self.jsapi_ticket_expiration_time
  367. and self.jsapi_ticket_expiration_time > datetime.now()
  368. ):
  369. # 未过期,
  370. # print("未过期")
  371. msg = {
  372. "title": _("Tips"),
  373. "message": _("Ticket is still valid, and no update is required!"),
  374. "sticky": False,
  375. }
  376. return self.env["wecomapi.tools.action"].WecomInfoNotification(msg)
  377. else:
  378. # print("过期")
  379. oec_im_wecom_api = self.env["wecom.service_api"].InitServiceApi(
  380. self.company_id.corpid, self.secret
  381. )
  382. response = oec_im_wecom_api.httpCall(
  383. self.env["wecom.service_api_list"].get_server_api_call(
  384. "AGENT_GET_TICKET"
  385. ),
  386. {"type": "agent_config"},
  387. )
  388. except ApiException as ex:
  389. return self.env["wecomapi.tools.action"].ApiExceptionDialog(
  390. ex, raise_exception=True
  391. )
  392. else:
  393. if response["errcode"] == 0:
  394. self.write(
  395. {
  396. "jsapi_ticket": response["ticket"],
  397. "jsapi_ticket_expiration_time": datetime.now()
  398. + timedelta(seconds=response["expires_in"]),
  399. }
  400. )
  401. msg = {
  402. "title": _("Tips"),
  403. "message": _("Successfully obtained application ticket!"),
  404. "sticky": False,
  405. "next": {
  406. "type": "ir.actions.client",
  407. "tag": "reload",
  408. },
  409. }
  410. finally:
  411. return self.env["wecomapi.tools.action"].WecomSuccessNotification(msg)
上海开阖软件有限公司 沪ICP备12045867号-1